<template>
  <div class="voice-control-interface">
    <!-- 顶部标题栏 -->
    <div class="header-bar">
      <div class="header-content">
      <div class="logo-section">
        <img src="@/assets/touxiang.png" alt="小七" class="logo-avatar" />
        <h1 class="app-title">{{ t('header.title') }}</h1>
      </div>
        
        <!-- MCP服务提示 -->
        <div class="mcp-tip-header">
          <el-icon class="tip-icon-header"><InfoFilled /></el-icon>
          <span class="tip-text-header">{{ t('quickActions.mcpTip') }}</span>
        </div>
        
        <!-- 右侧按钮组 -->
        <div class="header-actions">
          <!-- 设置按钮 -->
          <el-button 
            circle 
            size="default"
            @click="openSettings"
            class="header-settings-btn"
          >
            <el-icon size="18"><Setting /></el-icon>
          </el-button>
          
          <!-- 连接控制按钮 -->
          <el-button 
            ref="connectButton"
            :type="isConnected ? 'danger' : 'primary'"
            :loading="isConnecting"
            @click="toggleConnection"
            size="large"
          >
            <el-icon v-if="!isConnecting">
              <Connection v-if="!isConnected" />
              <Close v-else />
            </el-icon>
            {{ isConnecting ? t('header.connecting') : (isConnected ? t('header.disconnect') : t('header.connect')) }}
          </el-button>
        </div>
      </div>
    </div>

    <!-- 主内容区域 -->
    <div class="main-content">
      <!-- 左侧语音控制面板 -->
      <div class="left-panel">
        <div class="voice-section">
          <!-- 语音控制卡片 -->
          <div class="voice-card">
            <div class="card-header">
              <h3>{{ t('voiceInput.title') }}</h3>
              <p class="card-subtitle">{{ isConnected ? t('voiceInput.connectedSubtitle') : t('voiceInput.disconnectedSubtitle') }}</p>
            </div>
            
            <!-- 主控制圆圈 -->
            <div class="voice-circle-container">
              <div 
                class="voice-circle"
                :class="{
                  'recording': isRecording,
                  'processing': isProcessing,
                  'listening': isListening,
                  'connected': isConnected,
                  'disconnected': !isConnected
                }"
                :style="{
                  '--volume-scale': 1 + audioVolume / 200,
                  '--ripple-scale': 2 + audioVolume / 50,
                  '--wave-height': 16 + audioVolume / 5
                }"
              >
                <!-- 圆圈内图片 -->
                <div 
                  class="circle-avatar-wrapper"
                  @click="toggleConnection"
                  :class="{ 'clickable': !isConnecting && !isProcessing }"
                  :title="isConnected ? t('header.disconnect') : t('header.connect')"
                >
                  <img src="@/assets/touxiang.png" alt="小七" class="circle-avatar" />
                  <!-- 处理中的旋转加载动画 -->
                  <div v-if="isProcessing" class="processing-overlay">
                    <el-icon size="36" class="processing-icon">
                      <Loading />
                    </el-icon>
                  </div>
                  <!-- 连接中的加载动画 -->
                  <div v-if="isConnecting" class="connecting-overlay">
                    <el-icon size="36" class="connecting-icon">
                      <Loading />
                    </el-icon>
                  </div>
                </div>
                
                <!-- 波纹效果 -->
                <div v-if="isRecording" class="ripple-effect">
                  <div class="ripple"></div>
                  <div class="ripple"></div>
                  <div class="ripple"></div>
                </div>
              </div>
              
              <!-- 状态文字 -->
              <div class="status-text">
                <span v-if="!isConnected" class="disconnected-text">{{ t('voiceInput.pleaseConnect') }}</span>
                <span v-else-if="isTTSPlaying" class="tts-playing-text">{{ t('voiceInput.ttsPlaying') }}</span>
                <span v-else-if="isExecutingCommand" class="processing-text">
                  {{ t('voiceInput.executing') }}
                  <span class="execution-time">({{ commandExecutionTime }}s)</span>
                </span>
                <span v-else-if="isPausedForCommand" class="paused-text">{{ t('voiceInput.paused') }}</span>
                <span v-else-if="!isRecording && !isProcessing" class="listening-text">{{ t('voiceInput.listening') }}</span>
                <span v-else-if="isRecording" class="recording-text">{{ t('voiceInput.recording') }}</span>
                <span v-else-if="isProcessing" class="processing-text">{{ t('voiceInput.processing') }}</span>
              </div>
              
              <!-- 音量可视化指示器 -->
              <div v-if="isRecording" class="volume-indicator">
                <div class="volume-bar-container">
                  <div class="volume-bar" :style="{ width: audioVolume + '%' }"></div>
                </div>
                <span class="volume-text">{{ t('voiceInput.volume') }}: {{ Math.round(audioVolume) }}%</span>
              </div>
            </div>
            
            <!-- 小屏幕专用：当前对话显示（仅一行） -->
            <div v-if="latestMessage" class="current-dialog-text mobile-only">
              {{ latestMessage.text }}
              <span v-if="latestMessage.isStreaming" class="streaming-cursor">|</span>
            </div>
          </div>

          <!-- 推荐功能卡片 -->
          <div class="quick-actions-card">
            <div class="card-header">
              <h3>{{ t('quickActions.title') }}</h3>
            </div>
            <div class="quick-actions">
              <el-button 
                ref="firstQuickAction"
                type="primary" 
                plain
                @click="sendQuickCommand('播放稻香')"
                :disabled="isQuickCommandProcessing"
                :loading="isQuickCommandProcessing"
                class="quick-btn"
              >
                {{ t('quickActions.playMusic') }}
              </el-button>
              <el-button 
                type="primary" 
                plain
                @click="sendQuickCommand('给我订一个早上九点的闹钟')"
                :disabled="isQuickCommandProcessing"
                :loading="isQuickCommandProcessing"
                class="quick-btn"
              >
                {{ t('quickActions.setAlarm') }}
              </el-button>
              <el-button 
                type="primary" 
                plain
                @click="sendQuickCommand('给我讲一个笑话')"
                :disabled="isQuickCommandProcessing"
                :loading="isQuickCommandProcessing"
                class="quick-btn"
              >
                {{ t('quickActions.tellJoke') }}
              </el-button>
              <el-button 
                type="primary" 
                plain
                @click="sendQuickCommand('查一下后天杭州的天气')"
                :disabled="isQuickCommandProcessing"
                :loading="isQuickCommandProcessing"
                class="quick-btn"
              >
                {{ t('quickActions.checkWeather') }}
              </el-button>
              <el-button 
                type="primary" 
                plain
                @click="openComicDialog"
                :disabled="isQuickCommandProcessing"
                :loading="isQuickCommandProcessing"
                class="quick-btn"
              >
                {{ t('quickActions.generateComic') }}
              </el-button>
            </div>
          </div>
        </div>
      </div>

      <!-- 右侧聊天面板 -->
      <div class="right-panel">
        <div class="chat-card">
          <div class="chat-header">
            <h3>{{ t('chat.title') }}</h3>
            <el-button 
              type="text" 
              size="small" 
              @click="clearChat"
              :disabled="chatMessages.length === 0"
              class="clear-btn"
            >
              <el-icon><Delete /></el-icon>
              {{ t('chat.clear') }}
            </el-button>
          </div>
          
          <div class="chat-messages" ref="chatContainer">
            <div 
              v-for="message in chatMessages" 
              :key="message.id"
              class="message-item"
              :class="message.type"
            >
              <div class="message-avatar">
                <el-avatar 
                  v-if="message.type === 'user'" 
                  :size="32" 
                  class="user-avatar"
                >
                  <el-icon>
                    <User />
                  </el-icon>
                </el-avatar>
                <el-avatar 
                  v-else 
                  :size="32" 
                  class="ai-avatar"
                  :src="touxiangImg"
                />
              </div>
              
              <div class="message-content">
                <div class="message-bubble" :class="[message.type, { 'streaming': message.isStreaming }]">
                  <div class="message-text">
                    {{ message.text }}
                    <span v-if="message.isStreaming" class="streaming-indicator">...</span>
                  </div>
                  <div class="message-time">{{ formatTime(message.timestamp) }}</div>
                </div>
                
                <!-- 生成的图片展示 -->
                <div v-if="message.images && message.images.length > 0" class="message-images">
                  <div class="images-grid">
                    <div 
                      v-for="(image, index) in message.images" 
                      :key="index"
                      class="image-item"
                    >
                      <div class="image-number">{{ index + 1 }}</div>
                      <el-image
                        :src="image.url"
                        :preview-src-list="message.images.map(img => img.url)"
                        :initial-index="index"
                        fit="cover"
                        class="message-image"
                        lazy
                      >
                        <template #placeholder>
                          <div class="image-loading">
                            <el-icon><Loading /></el-icon>
                          </div>
                        </template>
                        <template #error>
                          <div class="image-error-small">
                            <el-icon><PictureFilled /></el-icon>
                          </div>
                        </template>
                      </el-image>
                    </div>
                  </div>
                </div>
                
                <!-- 指令执行结果 -->
                <div v-if="message.result" class="command-result">
                  <el-alert
                    :type="message.result.success ? 'success' : 'error'"
                    :title="message.result.success ? t('chat.executeSuccess') : t('chat.executeFailed')"
                    :description="message.result.message"
                    show-icon
                    :closable="false"
                  />
                </div>
              </div>
            </div>
            
            <!-- 空状态 -->
            <div v-if="chatMessages.length === 0" class="empty-chat">
              <el-empty 
                :description="t('chat.empty')"
                :image-size="120"
              >
                <template #image>
                  <el-icon size="80" class="empty-icon">
                    <ChatDotRound />
                  </el-icon>
                </template>
                <p>{{ t('chat.emptyHint') }}</p>
              </el-empty>
            </div>
          </div>
        </div>
      </div>
    </div>
    
    <!-- 设置对话框 -->
    <el-dialog
      v-model="settingsVisible"
      :title="t('settings.title')"
      width="500px"
      :modal="true"
    >
      <div class="settings-content">
        <!-- 主题设置 -->
        <div class="setting-item">
          <div class="setting-label">
            <el-icon><Sunny /></el-icon>
            <span>{{ t('settings.theme') }}</span>
          </div>
          <el-segmented v-model="themeMode" :options="themeOptions" size="default" />
        </div>
        
        <!-- 语言设置 -->
        <div class="setting-item">
          <div class="setting-label">
            <el-icon><PictureRounded /></el-icon>
            <span>{{ t('settings.language') }}</span>
          </div>
          <el-select v-model="language" placeholder="选择语言" style="width: 180px">
            <el-option label="简体中文" value="zh-CN" />
            <el-option label="English" value="en-US" />
            <el-option label="日本語" value="ja-JP" />
          </el-select>
        </div>
        
        <!-- 其他设置预留 -->
        <div class="setting-item">
          <div class="setting-label">
            <el-icon><Bell /></el-icon>
            <span>{{ t('settings.soundNotification') }}</span>
          </div>
          <el-switch v-model="soundEnabled" />
        </div>
      </div>
      
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="cancelSettings">{{ t('settings.cancel') }}</el-button>
          <el-button type="primary" @click="saveSettings">{{ t('settings.save') }}</el-button>
        </span>
      </template>
    </el-dialog>
    
    <!-- 生成漫画对话框 -->
    <el-dialog
      v-model="comicDialogVisible"
      title="🎨 生成漫画"
      width="600px"
      :modal="true"
    >
      <div class="comic-dialog-content">
        <el-form :model="comicForm" label-width="80px">
          <el-form-item label="漫画主题">
            <el-input
              v-model="comicForm.userInput"
              type="textarea"
              :rows="4"
              placeholder="请输入您想要生成的漫画内容，例如：一只勇敢的小猫咪去冒险的故事"
              maxlength="500"
              show-word-limit
            />
          </el-form-item>
          
          <el-form-item label="生成数量">
            <el-slider
              v-model="comicForm.numImages"
              :min="1"
              :max="5"
              :marks="{ 1: '1张', 2: '2张', 3: '3张', 4: '4张', 5: '5张' }"
              show-stops
            />
          </el-form-item>
          
          <el-alert
            :title="`将生成 ${comicForm.numImages} 张连续的漫画图片`"
            type="info"
            :closable="false"
            style="margin-bottom: 16px"
          />
        </el-form>
      </div>
      
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="comicDialogVisible = false" :disabled="isGeneratingComic">取消</el-button>
          <el-button 
            type="primary" 
            @click="generateComic" 
            :loading="isGeneratingComic"
            :disabled="!comicForm.userInput.trim()"
          >
            {{ isGeneratingComic ? '生成中...' : '开始生成' }}
          </el-button>
        </span>
      </template>
    </el-dialog>
    
    <!-- 漫画结果展示对话框 -->
    <el-dialog
      v-model="comicResultVisible"
      title="🎨 漫画生成结果"
      width="80%"
      :modal="true"
    >
      <div class="comic-result-content">
        <el-alert
          v-if="comicResult.success"
          :title="`成功生成 ${comicResult.images.length} 张漫画！`"
          type="success"
          :closable="false"
          style="margin-bottom: 20px"
        />
        
        <el-alert
          v-else
          title="生成失败"
          :description="comicResult.message"
          type="error"
          :closable="false"
          style="margin-bottom: 20px"
        />
        
        <div v-if="comicResult.success && comicResult.images.length > 0" class="comic-images-grid">
          <div 
            v-for="(image, index) in comicResult.images" 
            :key="index"
            class="comic-image-item"
          >
            <div class="comic-image-number">第 {{ index + 1 }} 张</div>
            <el-image
              :src="image.url"
              :preview-src-list="comicResult.images.map(img => img.url)"
              :initial-index="index"
              fit="cover"
              class="comic-image"
              lazy
            >
              <template #placeholder>
                <div class="image-placeholder">
                  <el-icon><Loading /></el-icon>
                  <span>加载中...</span>
                </div>
              </template>
              <template #error>
                <div class="image-error">
                  <el-icon><PictureFilled /></el-icon>
                  <span>加载失败</span>
                </div>
              </template>
            </el-image>
            <div class="comic-image-actions">
              <el-button 
                type="primary" 
                size="small" 
                @click="downloadImage(image.url, index + 1)"
                text
              >
                <el-icon><Download /></el-icon>
                下载
              </el-button>
            </div>
          </div>
        </div>
        
        <div v-if="comicResult.prompt" class="comic-prompt-info">
          <el-divider>生成提示词</el-divider>
          <el-text type="info" size="small">{{ comicResult.prompt }}</el-text>
        </div>
      </div>
      
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="comicResultVisible = false">关闭</el-button>
          <el-button type="primary" @click="openComicDialog">再次生成</el-button>
        </span>
      </template>
    </el-dialog>
    
    <!-- 新手引导遮罩 -->
    <div v-if="showGuide" class="guide-overlay" @click.stop="handleGuideClick">
      <!-- 四个遮罩区域（上下左右） -->
      <div 
        v-if="guideMaskStyles"
        class="guide-mask-top"
        :style="guideMaskStyles.top"
      ></div>
      <div 
        v-if="guideMaskStyles"
        class="guide-mask-right"
        :style="guideMaskStyles.right"
      ></div>
      <div 
        v-if="guideMaskStyles"
        class="guide-mask-bottom"
        :style="guideMaskStyles.bottom"
      ></div>
      <div 
        v-if="guideMaskStyles"
        class="guide-mask-left"
        :style="guideMaskStyles.left"
      ></div>
      
      <!-- 引导高亮框 -->
      <div 
        v-if="guideHighlightStyle"
        class="guide-highlight"
        :style="guideHighlightStyle"
      ></div>
      
      <!-- 引导提示框 -->
      <div 
        v-if="guideTooltipStyle"
        class="guide-tooltip"
        :style="guideTooltipStyle"
      >
        <div class="guide-tooltip-header">
          <span class="guide-step">{{ t('guide.step', { current: currentGuideStep, total: 2 }) }}</span>
          <el-button 
            type="text" 
            size="small" 
            @click.stop="skipGuide"
            class="guide-skip-btn"
          >
            {{ t('guide.skip') }}
          </el-button>
        </div>
        
        <div class="guide-tooltip-content">
          <h3>{{ currentGuideContent.title }}</h3>
          <p>{{ currentGuideContent.description }}</p>
        </div>
        
        <div class="guide-tooltip-footer">
          <el-button 
            v-if="currentGuideStep > 1"
            size="small"
            @click.stop="prevGuideStep"
          >
            {{ t('guide.prev') }}
          </el-button>
          <el-button 
            type="primary"
            size="small"
            @click.stop="nextGuideStep"
          >
            {{ currentGuideStep < 2 ? t('guide.next') : t('guide.finish') }}
          </el-button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'
import { ElMessage, ElNotification } from 'element-plus'
import { 
  Microphone, 
  Loading, 
  User, 
  Avatar, 
  CircleCheck, 
  CircleClose, 
  ChatDotRound,
  Connection,
  Close,
  VideoPlay,
  Edit,
  FolderOpened,
  Delete,
  Setting,
  Sunny,
  PictureRounded,
  Bell,
  InfoFilled,
  Download,
  PictureFilled
} from '@element-plus/icons-vue'
import { io } from 'socket.io-client'
import { useI18n } from 'vue-i18n'
import touxiangImg from '@/assets/touxiang.png'

// 多语言支持
const { t, locale } = useI18n()

// 响应式数据
const isRecording = ref(false)
const isProcessing = ref(false)
const isListening = ref(false)
const isConnected = ref(false)
const isTTSPlaying = ref(false)  // 🔊 TTS播放状态
const isConnecting = ref(false)
const chatMessages = ref([])
const chatContainer = ref(null)
const isStreamingASR = ref(false)  // 是否正在使用流式ASR
const streamingMessage = ref(null)  // 当前流式识别的消息
const audioVolume = ref(0)  // 当前音量（0-100）
const isExecutingCommand = ref(false)  // 是否正在执行命令
const isPausedForCommand = ref(false)  // 是否因执行命令而暂停录音
const isQuickCommandProcessing = ref(false)  // 快捷指令处理状态（独立于语音按钮）
const commandExecutionTime = ref(0)  // 命令执行时间（秒）
let commandExecutionTimer = null  // 命令执行计时器

// 新手引导相关
const showGuide = ref(false)  // 是否显示引导
const currentGuideStep = ref(1)  // 当前引导步骤（1或2）
const guideHighlightStyle = ref(null)  // 高亮框样式
const guideTooltipStyle = ref(null)  // 提示框样式
const guideMaskStyles = ref(null)  // 四个遮罩区域的样式
const connectButton = ref(null)  // 连接按钮引用
const firstQuickAction = ref(null)  // 第一个快捷指令按钮引用

// 自动TTS播报相关
const autoTTSAudioChunks = ref([])  // 自动播报的音频块
const autoTTSAudioContext = ref(null)  // 音频上下文
const autoTTSAudioElement = ref(null)  // 音频播放元素

// 设置相关
const settingsVisible = ref(false)  // 设置对话框显示状态
const themeMode = ref('light')  // 主题模式：light / dark / auto
const themeOptions = ref([
  { label: t('settings.themeLight'), value: 'light' },
  { label: t('settings.themeDark'), value: 'dark' },
  { label: t('settings.themeAuto'), value: 'auto' }
])
const language = ref('zh-CN')  // 语言设置
const soundEnabled = ref(true)  // 声音提示
const autoVoicePlayback = ref(true)  // 自动语音播报执行结果（默认开启）

// 生成漫画相关
const comicDialogVisible = ref(false)  // 漫画生成对话框显示状态
const comicResultVisible = ref(false)  // 漫画结果对话框显示状态
const isGeneratingComic = ref(false)  // 是否正在生成漫画
const comicForm = ref({
  userInput: '',  // 用户输入的漫画主题
  numImages: 3    // 生成数量，默认3张
})
const comicResult = ref({
  success: false,
  message: '',
  images: [],
  prompt: ''
})

// 监听语言变化，更新主题选项标签和欢迎消息
watch(locale, () => {
  themeOptions.value = [
    { label: t('settings.themeLight'), value: 'light' },
    { label: t('settings.themeDark'), value: 'dark' },
    { label: t('settings.themeAuto'), value: 'auto' }
  ]
  
  // 更新欢迎消息（第一条AI消息）
  if (chatMessages.value.length > 0 && chatMessages.value[0].type === 'ai') {
    chatMessages.value[0].text = t('messages.welcome')
    console.log('🌐 已更新欢迎消息为:', locale.value)
  }
})

// 临时设置（用于取消恢复）
const tempSettings = ref({})

// 引导内容配置
const currentGuideContent = ref({
  title: '',
  description: ''
})

// 打开设置对话框
const openSettings = () => {
  // 保存当前设置，以便取消时恢复
  tempSettings.value = {
    themeMode: themeMode.value,
    language: language.value,
    soundEnabled: soundEnabled.value,
    autoVoicePlayback: autoVoicePlayback.value
  }
  settingsVisible.value = true
}

// 取消设置
const cancelSettings = () => {
  // 恢复之前的设置
  themeMode.value = tempSettings.value.themeMode
  language.value = tempSettings.value.language
  soundEnabled.value = tempSettings.value.soundEnabled
  autoVoicePlayback.value = tempSettings.value.autoVoicePlayback
  settingsVisible.value = false
}

// ============= 新手引导相关函数 =============

// 检查并显示新手引导
const checkAndShowGuide = () => {
  // 检查本地存储，判断是否已完成引导
  const hasCompletedGuide = localStorage.getItem('guide-completed')
  
  if (!hasCompletedGuide) {
    // 延迟1秒后显示引导，确保页面元素已渲染
    setTimeout(() => {
      showGuide.value = true
      currentGuideStep.value = 1
      updateGuidePosition()
    }, 1000)
  }
}

// 更新引导高亮位置
const updateGuidePosition = () => {
  nextTick(() => {
    if (currentGuideStep.value === 1) {
      // 第一步：高亮连接按钮
      if (connectButton.value && connectButton.value.$el) {
        const rect = connectButton.value.$el.getBoundingClientRect()
        const viewportWidth = window.innerWidth
        const viewportHeight = window.innerHeight
        const tooltipWidth = 360 // 提示框宽度（含padding）
        
        // 设置高亮框位置和大小
        const highlightTop = rect.top - 8
        const highlightLeft = rect.left - 8
        const highlightWidth = rect.width + 16
        const highlightHeight = rect.height + 16
        
        guideHighlightStyle.value = {
          top: `${highlightTop}px`,
          left: `${highlightLeft}px`,
          width: `${highlightWidth}px`,
          height: `${highlightHeight}px`
        }
        
        // 设置四个遮罩区域
        guideMaskStyles.value = {
          top: {
            top: '0',
            left: '0',
            right: '0',
            height: `${highlightTop}px`
          },
          right: {
            top: `${highlightTop}px`,
            left: `${highlightLeft + highlightWidth}px`,
            right: '0',
            height: `${highlightHeight}px`
          },
          bottom: {
            top: `${highlightTop + highlightHeight}px`,
            left: '0',
            right: '0',
            bottom: '0'
          },
          left: {
            top: `${highlightTop}px`,
            left: '0',
            width: `${highlightLeft}px`,
            height: `${highlightHeight}px`
          }
        }
        
        // 智能计算提示框位置（优先下方，超出则左侧）
        let tooltipTop = rect.bottom + 20
        let tooltipLeft = rect.left + rect.width / 2
        let transform = 'translateX(-50%)'
        
        // 检查提示框是否会超出右侧
        if (tooltipLeft + tooltipWidth / 2 > viewportWidth - 20) {
          // 超出右侧，改为居中或靠左
          tooltipLeft = Math.min(tooltipLeft, viewportWidth - tooltipWidth / 2 - 20)
        }
        
        // 检查提示框是否会超出左侧
        if (tooltipLeft - tooltipWidth / 2 < 20) {
          tooltipLeft = tooltipWidth / 2 + 20
        }
        
        // 检查提示框是否会超出底部
        if (tooltipTop + 200 > viewportHeight) {
          // 超出底部，改为按钮上方
          tooltipTop = rect.top - 20
          transform = 'translate(-50%, -100%)'
        }
        
        guideTooltipStyle.value = {
          top: `${tooltipTop}px`,
          left: `${tooltipLeft}px`,
          transform: transform
        }
        
        // 更新引导内容
        currentGuideContent.value = {
          title: t('guide.step1Title'),
          description: t('guide.step1Description')
        }
      }
    } else if (currentGuideStep.value === 2) {
      // 第二步：高亮第一个快捷指令
      if (firstQuickAction.value && firstQuickAction.value.$el) {
        const rect = firstQuickAction.value.$el.getBoundingClientRect()
        const viewportWidth = window.innerWidth
        const viewportHeight = window.innerHeight
        const tooltipWidth = 360
        
        // 设置高亮框位置和大小
        const highlightTop = rect.top - 8
        const highlightLeft = rect.left - 8
        const highlightWidth = rect.width + 16
        const highlightHeight = rect.height + 16
        
        guideHighlightStyle.value = {
          top: `${highlightTop}px`,
          left: `${highlightLeft}px`,
          width: `${highlightWidth}px`,
          height: `${highlightHeight}px`
        }
        
        // 设置四个遮罩区域
        guideMaskStyles.value = {
          top: {
            top: '0',
            left: '0',
            right: '0',
            height: `${highlightTop}px`
          },
          right: {
            top: `${highlightTop}px`,
            left: `${highlightLeft + highlightWidth}px`,
            right: '0',
            height: `${highlightHeight}px`
          },
          bottom: {
            top: `${highlightTop + highlightHeight}px`,
            left: '0',
            right: '0',
            bottom: '0'
          },
          left: {
            top: `${highlightTop}px`,
            left: '0',
            width: `${highlightLeft}px`,
            height: `${highlightHeight}px`
          }
        }
        
        // 智能计算提示框位置（优先右侧，超出则下方）
        let tooltipLeft = rect.right + 20
        let tooltipTop = rect.top + rect.height / 2
        let transform = 'translateY(-50%)'
        
        // 检查提示框是否会超出右侧
        if (tooltipLeft + tooltipWidth > viewportWidth - 20) {
          // 超出右侧，改为下方
          tooltipLeft = rect.left + rect.width / 2
          tooltipTop = rect.bottom + 20
          transform = 'translateX(-50%)'
          
          // 检查是否会超出底部
          if (tooltipTop + 200 > viewportHeight) {
            // 超出底部，改为上方
            tooltipTop = rect.top - 20
            transform = 'translate(-50%, -100%)'
          }
        }
        
        guideTooltipStyle.value = {
          top: `${tooltipTop}px`,
          left: `${tooltipLeft}px`,
          transform: transform
        }
        
        // 更新引导内容
        currentGuideContent.value = {
          title: t('guide.step2Title'),
          description: t('guide.step2Description')
        }
      }
    }
  })
}

// 下一步引导
const nextGuideStep = () => {
  if (currentGuideStep.value < 2) {
    currentGuideStep.value++
    updateGuidePosition()
  } else {
    // 完成引导
    finishGuide()
  }
}

// 上一步引导
const prevGuideStep = () => {
  if (currentGuideStep.value > 1) {
    currentGuideStep.value--
    updateGuidePosition()
  }
}

// 跳过引导
const skipGuide = () => {
  finishGuide()
}

// 完成引导
const finishGuide = () => {
  showGuide.value = false
  guideHighlightStyle.value = null
  guideTooltipStyle.value = null
  guideMaskStyles.value = null
  
  // 保存到本地存储，下次不再显示
  localStorage.setItem('guide-completed', 'true')
  
  ElMessage.success(t('guide.completed'))
}

// 点击遮罩层（不做任何操作，用户必须点击"下一步"或"跳过"）
const handleGuideClick = (e) => {
  // 阻止点击事件传播，避免意外触发其他元素
  e.stopPropagation()
}

// 监听窗口大小变化，更新引导位置
const handleResize = () => {
  if (showGuide.value) {
    updateGuidePosition()
  }
}

// ============= 新手引导相关函数结束 =============

// ============= 命令执行计时器函数 =============

// 启动命令执行计时器
const startCommandExecutionTimer = () => {
  // 清除之前的计时器（如果存在）
  if (commandExecutionTimer) {
    clearInterval(commandExecutionTimer)
  }
  
  // 重置时间为0
  commandExecutionTime.value = 0
  
  // 每秒更新一次
  commandExecutionTimer = setInterval(() => {
    commandExecutionTime.value++
  }, 1000)
  
  console.log('⏱️ 命令执行计时器已启动')
}

// 停止命令执行计时器
const stopCommandExecutionTimer = () => {
  if (commandExecutionTimer) {
    clearInterval(commandExecutionTimer)
    commandExecutionTimer = null
    console.log(`⏱️ 命令执行计时器已停止，总耗时: ${commandExecutionTime.value}秒`)
  }
  
  // 重置时间
  commandExecutionTime.value = 0
}

// ============= 命令执行计时器函数结束 =============

// WebSocket连接
let socket = null
let mediaRecorder = null
let audioChunks = []
let streamingAudioContext = null
let streamingMediaRecorder = null
let volumeAnalyser = null  // 音量分析器
let volumeCheckInterval = null  // 音量检测定时器
let asrErrorRecoveryTimer = null  // ASR错误恢复定时器
let lastAsrErrorTime = 0  // 最后一次ASR错误时间

// 命令执行去重
const lastExecutedCommand = ref({
  text: '',
  timestamp: 0
})

// 初始化
// 页面可见性状态
const wasRecordingBeforeHidden = ref(false)
const pageVisibilityTimeout = ref(null)

// 处理页面可见性变化
const handleVisibilityChange = () => {
  if (document.hidden) {
    // 页面隐藏（离开页面）
    console.log('📴 页面隐藏 - 暂停录音')
    
    // 记录当前是否在录音
    wasRecordingBeforeHidden.value = isStreamingASR.value
    
    // 如果正在流式ASR，停止
    if (isStreamingASR.value) {
      console.log('🛑 因页面隐藏，停止流式ASR')
      stopStreamingASR()
    }
    
    // 清除任何待恢复的定时器
    if (pageVisibilityTimeout.value) {
      clearTimeout(pageVisibilityTimeout.value)
      pageVisibilityTimeout.value = null
    }
  } else {
    // 页面可见（回到页面）
    console.log('📱 页面可见 - 准备恢复录音')
    
    // 如果之前在录音，延迟0.5秒后恢复
    if (wasRecordingBeforeHidden.value && isConnected.value) {
      console.log('⏳ 0.5秒后恢复流式ASR')
      
      pageVisibilityTimeout.value = setTimeout(() => {
        console.log('🔄 恢复流式ASR')
        startStreamingASR()
        wasRecordingBeforeHidden.value = false
      }, 500) // 延迟0.5秒
    }
  }
}

onMounted(() => {
  // 不自动连接WebSocket，等待用户点击连接按钮
  checkMicrophonePermission()
  
  // 加载保存的设置
  loadSettings()
  
  // 显示默认欢迎消息（无论是否连接）
  const welcomeMessage = t('messages.welcome')
  
  addMessage('ai', welcomeMessage)
  console.log('✅ 已显示欢迎消息')
  
  // 监听页面可见性变化
  document.addEventListener('visibilitychange', handleVisibilityChange)
  console.log('✅ 已添加页面可见性监听')
  
  // 监听窗口大小变化（用于更新引导位置）
  window.addEventListener('resize', handleResize)
  
  // 检查是否是新用户，显示引导
  checkAndShowGuide()
})

onUnmounted(() => {
  // 移除页面可见性监听
  document.removeEventListener('visibilitychange', handleVisibilityChange)
  
  // 移除窗口大小变化监听
  window.removeEventListener('resize', handleResize)
  
  // 清除定时器
  if (pageVisibilityTimeout.value) {
    clearTimeout(pageVisibilityTimeout.value)
  }
  
  // 清除命令执行计时器
  stopCommandExecutionTimer()
  
  console.log('✅ 已移除页面可见性监听')
  
  disconnectWebSocket()
  stopAutoRecording()
})

// 设置相关函数
const loadSettings = () => {
  try {
    const savedTheme = localStorage.getItem('theme-mode')
    const savedLanguage = localStorage.getItem('language')
    const savedSound = localStorage.getItem('sound-enabled')
    const savedAutoVoicePlayback = localStorage.getItem('auto-voice-playback')
    
    console.log('📂 加载保存的设置:', {
      savedTheme,
      savedLanguage,
      savedSound,
      savedAutoVoicePlayback
    })
    
    if (savedTheme) themeMode.value = savedTheme
    if (savedLanguage) {
      language.value = savedLanguage
      locale.value = savedLanguage  // 应用语言设置
    }
    if (savedSound !== null) soundEnabled.value = savedSound === 'true'
    if (savedAutoVoicePlayback !== null) autoVoicePlayback.value = savedAutoVoicePlayback === 'true'
    
    console.log('✅ 设置加载完成:', {
      themeMode: themeMode.value,
      language: language.value,
      locale: locale.value,
      soundEnabled: soundEnabled.value,
      autoVoicePlayback: autoVoicePlayback.value
    })
    
    // 应用主题
    applyTheme(themeMode.value)
  } catch (error) {
    console.error('❌ 加载设置失败:', error)
  }
}

const saveSettings = () => {
  try {
    localStorage.setItem('theme-mode', themeMode.value)
    localStorage.setItem('language', language.value)
    localStorage.setItem('sound-enabled', soundEnabled.value.toString())
    localStorage.setItem('auto-voice-playback', autoVoicePlayback.value.toString())
    
    console.log('💾 设置已保存:', {
      themeMode: themeMode.value,
      language: language.value,
      soundEnabled: soundEnabled.value,
      autoVoicePlayback: autoVoicePlayback.value
    })
    
    // 应用主题
    applyTheme(themeMode.value)
    
    // 切换语言
    locale.value = language.value
    console.log('🌐 语言已切换为:', language.value)
    
    // 声音提示反馈
    if (soundEnabled.value) {
      console.log('🔔 声音提示已开启')
    } else {
      console.log('🔕 声音提示已关闭')
    }
    
    // 自动播报反馈
    if (autoVoicePlayback.value) {
      console.log('🔊 自动语音播报已开启')
    } else {
      console.log('🔇 自动语音播报已关闭')
    }
    
    ElMessage.success(t('settings.saved'))
    settingsVisible.value = false
  } catch (error) {
    console.error('保存设置失败:', error)
    ElMessage.error(t('settings.saveFailed'))
  }
}

const applyTheme = (mode) => {
  const html = document.documentElement
  
  console.log('🎨 应用主题:', mode)
  
  if (mode === 'dark') {
    html.classList.add('dark-theme')
    html.classList.remove('light-theme')
    console.log('✅ 已切换到深色主题')
  } else if (mode === 'light') {
    html.classList.add('light-theme')
    html.classList.remove('dark-theme')
    console.log('✅ 已切换到浅色主题')
  } else if (mode === 'auto') {
    // 跟随系统
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
    if (prefersDark) {
      html.classList.add('dark-theme')
      html.classList.remove('light-theme')
      console.log('✅ 已切换到深色主题（跟随系统）')
    } else {
      html.classList.add('light-theme')
      html.classList.remove('dark-theme')
      console.log('✅ 已切换到浅色主题（跟随系统）')
    }
  }
  
  // 记录当前 HTML 的 class
  console.log('📋 当前HTML类名:', html.className)
}

// 监听主题变化 - 实时预览
watch(themeMode, (newMode) => {
  console.log('👀 主题模式改变:', newMode)
  applyTheme(newMode)
})

// 监听系统主题变化（当设置为auto时）
if (window.matchMedia) {
  const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)')
  darkModeQuery.addEventListener('change', (e) => {
    console.log('🖥️ 系统主题改变:', e.matches ? 'dark' : 'light')
    if (themeMode.value === 'auto') {
      applyTheme('auto')
    }
  })
}

// WebSocket初始化
const initWebSocket = () => {
  socket = io('http://localhost:18080', {
    transports: ['websocket', 'polling'],
    timeout: 5000,
    reconnectionAttempts: 5,
    reconnectionDelay: 1000
  })

  socket.on('connect', () => {
    isConnected.value = true
    isConnecting.value = false
    ElMessage.success(t('messages.connectSuccess'))
    console.log('WebSocket连接成功')
    
    // 连接成功后自动启动流式ASR识别
    startStreamingASR()
  })

  socket.on('disconnect', () => {
    isConnected.value = false
    ElMessage.warning(t('messages.disconnected'))
    console.log('WebSocket连接断开')
    
    // 清理ASR错误恢复定时器
    if (asrErrorRecoveryTimer) {
      clearTimeout(asrErrorRecoveryTimer)
      asrErrorRecoveryTimer = null
    }
  })

  socket.on('connect_error', (error) => {
    isConnected.value = false
    isConnecting.value = false
    ElMessage.error(t('messages.connectFailed'))
    console.error('WebSocket连接错误:', error)
  })

  // 监听ASR识别结果
  socket.on('asr_result', (data) => {
    console.log('收到ASR结果:', data)
    if (data.text) {
      addMessage('user', data.text)
    }
  })

  // 监听指令处理结果
  socket.on('command_processed', (data) => {
    console.log('收到指令处理结果:', data)
    const lastMessage = chatMessages.value[chatMessages.value.length - 1]
    if (lastMessage && lastMessage.type === 'user') {
      // 优先使用execution_result中的message（包含实际的执行结果）
      const executionMessage = data.data?.execution_result?.message
      lastMessage.result = {
        success: data.data.success,
        message: executionMessage || (data.data.success ? '指令执行成功' : '指令执行失败')
      }
      
      // 🔊 自动播报执行结果
      if (data.data.success && executionMessage && autoVoicePlayback.value) {
        console.log('🔊 [TTS] 播报执行结果:', executionMessage)
        triggerAutoTTS(executionMessage)
      }
    }
    isProcessing.value = false
  })

  // 监听语音控制事件
  socket.on('voice_control_started', () => {
    ElNotification({
      title: '语音控制',
      message: '语音控制已启动',
      type: 'success'
    })
  })

  socket.on('voice_control_stopped', () => {
    ElNotification({
      title: '语音控制',
      message: '语音控制已停止',
      type: 'info'
    })
  })

  // 流式ASR事件监听
  socket.on('streaming_asr_started', (data) => {
    console.log('流式ASR已启动:', data)
    isStreamingASR.value = true
  })

  socket.on('streaming_asr_ready', (data) => {
    console.log('流式ASR已就绪:', data)
    ElMessage.success(t('messages.asrReady'))
    // 开始流式音频采集
    startStreamingAudioCapture()
  })

  socket.on('streaming_asr_partial', (data) => {
    console.log('流式ASR中间结果:', data)
    // 更新或创建流式识别消息
    updateStreamingMessage(data.text, false)
  })

  socket.on('streaming_asr_final', (data) => {
    console.log('流式ASR最终结果:', data)
    // 固定最终结果
    updateStreamingMessage(data.text, true)
    
    // 🔇 暂停录音，准备执行命令
    if (isStreamingASR.value && !isPausedForCommand.value) {
      console.log('🔇 暂停录音以执行命令')
      pauseAudioCapture()
      isPausedForCommand.value = true
      isExecutingCommand.value = true
      // ⏱️ 启动命令执行计时器
      startCommandExecutionTimer()
    }
  })

  socket.on('streaming_asr_error', (data) => {
    console.error('❌ 流式ASR错误详情:', JSON.stringify(data, null, 2))
    console.error('错误类型:', data.type)
    console.error('错误消息:', data.message)
    console.error('时间戳:', data.timestamp)
    
    // 🔧 兜底逻辑：清理错误状态，防止影响下一轮
    console.log('🔧 [兜底] 清理错误状态，准备恢复...')
    isPausedForCommand.value = false
    isExecutingCommand.value = false
    isTTSPlaying.value = false
    // ⏱️ 停止命令执行计时器
    stopCommandExecutionTimer()
    
    // 🔴 如果是会话失效错误，立即标记ASR已停止
    const isSessionExpired = data.message && (
      data.message.includes('会话已失效') || 
      data.message.includes('Timeout waiting') ||
      data.message.includes('session already finished')
    )
    
    if (isSessionExpired) {
      console.log('🔴 [会话失效] 检测到会话超时/失效，标记ASR已停止')
      isStreamingASR.value = false
      stopStreamingAudioCapture()
      
      // 不显示错误消息（会话超时是正常情况）
      console.log('ℹ️ [会话失效] 等待后端清理session后自动恢复...')
      
      // 会话失效不受10秒限制，可以立即重启
      // 等待后端发送 streaming_asr_stopped 事件后再重启（见下方事件处理）
      return
    }
    
    // 只在严重错误时显示提示，避免频繁弹窗
    if (data.message && !data.message.includes('重复') && !data.message.includes('停止失败')) {
      ElMessage.error(t('messages.asrError') + ': ' + data.message)
    }
    
    // 🔄 自动恢复机制：避免频繁重启（仅用于非会话失效的错误）
    const now = Date.now()
    const timeSinceLastError = now - lastAsrErrorTime
    lastAsrErrorTime = now
    
    // 如果距离上次错误超过10秒，或者ASR确实已停止，则尝试恢复
    if (timeSinceLastError > 10000 || !isStreamingASR.value) {
      // 清除之前的恢复定时器
      if (asrErrorRecoveryTimer) {
        clearTimeout(asrErrorRecoveryTimer)
        asrErrorRecoveryTimer = null
      }
      
      if (isConnected.value && !isStreamingASR.value && !isTTSPlaying.value) {
        console.log('🔄 [兜底] 3秒后自动重启ASR会话...')
        asrErrorRecoveryTimer = setTimeout(() => {
          if (isConnected.value && !isStreamingASR.value && !isTTSPlaying.value) {
            console.log('🚀 [兜底] 开始自动恢复ASR会话')
            startStreamingASR()
          }
          asrErrorRecoveryTimer = null
        }, 3000)
      }
    } else {
      console.log('⚠️ [兜底] 错误频繁，跳过自动重启（距上次错误仅 ' + Math.round(timeSinceLastError / 1000) + ' 秒）')
    }
  })

  socket.on('streaming_asr_stopped', (data) => {
    console.log('流式ASR已停止:', data)
    isStreamingASR.value = false
    stopStreamingAudioCapture()
    
    // 清空命令去重缓存（停止后可以重新识别相同命令）
    lastExecutedCommand.value.text = ''
    lastExecutedCommand.value.timestamp = 0
    
    // 🔄 如果是会话失效导致的停止，自动重启ASR
    if (data && data.reason === 'session_expired') {
      console.log('🔄 [会话恢复] 后端已清理失效session，2秒后自动重启ASR...')
      
      // 清除之前的恢复定时器
      if (asrErrorRecoveryTimer) {
        clearTimeout(asrErrorRecoveryTimer)
        asrErrorRecoveryTimer = null
      }
      
      // 2秒后重启（给后端足够的清理时间）
      asrErrorRecoveryTimer = setTimeout(() => {
        if (isConnected.value && !isStreamingASR.value && !isTTSPlaying.value) {
          console.log('🚀 [会话恢复] 开始自动重启ASR会话')
          startStreamingASR()
        } else {
          console.log('⚠️ [会话恢复] 当前状态不适合重启ASR')
          console.log('   isConnected:', isConnected.value)
          console.log('   isStreamingASR:', isStreamingASR.value)
          console.log('   isTTSPlaying:', isTTSPlaying.value)
        }
        asrErrorRecoveryTimer = null
      }, 2000)
    }
  })

  // 流式LLM回复相关的状态
  let currentStreamingAIMessage = null
  
  // 监听LLM流式回复开始
  socket.on('llm_response_start', (data) => {
    console.log('🌊 LLM流式回复开始:', data)
    
    // 创建一个新的AI消息用于接收流式内容
    currentStreamingAIMessage = {
      id: 'ai-streaming-' + Date.now(),
      type: 'ai',
      text: '',
      isStreaming: true,  // 标记为流式生成中
      timestamp: new Date(),
      metadata: {
        original_text: data.original_text,
        error_message: data.error_message
      }
    }
    chatMessages.value.push(currentStreamingAIMessage)
    
    // 滚动到底部
    nextTick(() => {
      if (chatContainer.value) {
        chatContainer.value.scrollTop = chatContainer.value.scrollHeight
      }
    })
  })
  
  // 监听LLM流式回复片段
  socket.on('llm_response_chunk', (data) => {
    console.log('📝 LLM流式片段:', data.chunk, '累积文本长度:', data.accumulated.length)
    
    if (currentStreamingAIMessage) {
      // 更新流式消息的文本 - 确保触发响应式更新
      currentStreamingAIMessage.text = data.accumulated
      
      // 强制触发响应式更新（如果需要）
      const index = chatMessages.value.findIndex(msg => msg.id === currentStreamingAIMessage.id)
      if (index !== -1) {
        chatMessages.value[index] = { ...chatMessages.value[index], text: data.accumulated }
      }
      
      // 滚动到底部，确保用户看到最新内容
      nextTick(() => {
        if (chatContainer.value) {
          chatContainer.value.scrollTop = chatContainer.value.scrollHeight
        }
      })
    } else {
      console.warn('⚠️ currentStreamingAIMessage 为空，无法更新流式消息')
    }
  })
  
  // 监听LLM流式回复结束
  socket.on('llm_response_end', (data) => {
    console.log('✅ LLM流式回复结束:', data)
    
    if (currentStreamingAIMessage) {
      // 固定最终结果 - 先更新引用
      currentStreamingAIMessage.text = data.full_text
      currentStreamingAIMessage.isStreaming = false
      
      // 强制触发响应式更新，确保动画停止
      const index = chatMessages.value.findIndex(msg => msg.id === currentStreamingAIMessage.id)
      if (index !== -1) {
        chatMessages.value[index] = { 
          ...chatMessages.value[index], 
          text: data.full_text,
          isStreaming: false  // ✅ 关键：停止动画
        }
        console.log('✅ 已停止流式动画')
      }
      
      currentStreamingAIMessage = null  // 清空引用
      
      // 不显示通知，大模型已经给出了友好回复
    }
    
    // 🔄 命令执行完成，重新启动流式ASR会话
    isExecutingCommand.value = false
    // ⏱️ 停止命令执行计时器
    stopCommandExecutionTimer()
    
    if (isPausedForCommand.value && isStreamingASR.value) {
      console.log('🔄 延迟2秒后重新启动流式ASR会话')
      // 延迟2秒，避免命令执行声音被识别
      setTimeout(() => {
        if (isStreamingASR.value && !isExecutingCommand.value) {
          console.log('🔄 重启流式ASR会话...')
          restartStreamingASR()
          isPausedForCommand.value = false
          console.log('✅ 流式ASR会话已重启')
        }
      }, 2000)
    }
  })
  
  // 监听命令执行结果（成功的情况或流式回复失败的备用方案）
  socket.on('command_executed', (data) => {
    console.log('命令执行结果:', data)
    
    // 去重检查：如果是相同的命令且在3秒内，跳过
    const currentTime = Date.now()
    if (data.original_text === lastExecutedCommand.value.text && 
        currentTime - lastExecutedCommand.value.timestamp < 3000) {
      console.log('⚠️ [前端去重] 跳过重复的命令执行结果')
      return
    }
    
    // 更新执行记录
    lastExecutedCommand.value.text = data.original_text
    lastExecutedCommand.value.timestamp = currentTime
    
    // 添加AI回复消息显示执行结果
    let resultMessage
    if (data.success) {
      // 成功时显示简洁的成功消息
      resultMessage = `✅ ${data.message}`
      
      addMessage('ai', resultMessage, {
        success: data.success,
        message: data.message,
        command_type: data.command_type,
        original_text: data.original_text
      })
      
      // 显示成功通知
      ElMessage.success(t('messages.commandSuccess'))
    } else {
      // 失败时，如果有friendly_reply说明是备用方案
      if (data.friendly_reply) {
        resultMessage = data.friendly_reply
        
        addMessage('ai', resultMessage, {
          success: data.success,
          message: data.message,
          friendly_reply: data.friendly_reply,
          command_type: data.command_type,
          original_text: data.original_text
        })
        
        // 不显示警告提示，友好回复已经在聊天界面中显示
      }
      // 否则说明已经通过流式回复处理了，不需要重复添加消息
    }
    
    // 🔄 命令执行完成，重新启动流式ASR会话（而不是简单恢复录音）
    isExecutingCommand.value = false
    // ⏱️ 停止命令执行计时器
    stopCommandExecutionTimer()
    
    if (isPausedForCommand.value && isStreamingASR.value) {
      console.log('🔄 命令执行完成，延迟2秒后重启流式ASR会话')
      // 延迟2秒，避免命令执行声音被识别
      setTimeout(() => {
        if (isStreamingASR.value && !isExecutingCommand.value) {
          console.log('🔄 重启流式ASR会话...')
          // 停止旧会话
          restartStreamingASR()
          isPausedForCommand.value = false
          console.log('✅ 流式ASR会话已重启')
        }
      }, 2000)
    }
  })
  
  // ============= TTS播报监听器 =============
  
  // 监听流式TTS音频块
  socket.on('streaming_tts_audio_chunk', (data) => {
    console.log(`🔊 [TTS] 收到音频块 ${data.chunk_index}`)
    
    // 将base64音频数据解码并保存
    const audioData = base64ToArrayBuffer(data.audio_data)
    autoTTSAudioChunks.value.push(audioData)
    
    // ⚠️ 不要在收到第一个音频块时就播放，等待所有音频块到达后再播放
    // 这样可以避免只播放一部分内容的问题
    if (data.chunk_index === 1) {
      console.log('🎵 [TTS] 开始接收音频块...')
    }
  })
  
  // 监听流式TTS完成
  socket.on('streaming_tts_complete', (data) => {
    console.log(`✅ [TTS] 合成完成，共收到 ${autoTTSAudioChunks.value.length} 个音频块`)
    // 合成完成后，播放所有音频块
    if (autoTTSAudioChunks.value.length > 0) {
      setTimeout(() => {
        playAutoTTSAudio()
      }, 100)
    } else {
      console.warn('⚠️ [TTS] 没有收到音频数据')
    }
  })
  
  // 监听TTS错误
  socket.on('streaming_tts_error', (data) => {
    console.error('❌ [TTS] 错误:', data.message || data.error)
    ElMessage.error({
      message: `TTS播报失败: ${data.message || data.error}`,
      duration: 2000
    })
  })
  
  // 监听TTS警告（如文本截断）
  socket.on('tts_warning', (data) => {
    console.warn('⚠️ [TTS] 警告:', data.message)
    // 警告信息已经在triggerAutoTTS中显示，这里只记录日志
  })
  
  // ============= 自动TTS播报监听器（兼容旧版） =============
  
  // 监听自动TTS音频块
  socket.on('auto_tts_audio_chunk', (data) => {
    console.log(`🔊 [自动播报] 收到音频块 ${data.chunk_index}`)
    
    // 将base64音频数据解码并保存
    const audioData = base64ToArrayBuffer(data.audio_data)
    autoTTSAudioChunks.value.push(audioData)
    
    // ⚠️ 不要在收到第一个音频块时就播放，等待所有音频块到达后再播放
    // 这样可以避免只播放一部分内容的问题
    if (data.chunk_index === 1) {
      console.log('🎵 [自动播报] 开始接收音频块...')
    }
  })
  
  // 监听自动TTS播报完成
  socket.on('auto_tts_complete', (data) => {
    console.log(`✅ [自动播报] 播报完成，共收到 ${autoTTSAudioChunks.value.length} 个音频块`)
    // 合成完成后，播放所有音频块
    if (autoTTSAudioChunks.value.length > 0) {
      setTimeout(() => {
        playAutoTTSAudio()
      }, 100)
    } else {
      console.warn('⚠️ [自动播报] 没有收到音频数据')
    }
  })
  
  // 监听自动TTS错误
  socket.on('auto_tts_error', (data) => {
    console.error('❌ [自动播报] 错误:', data.message)
  })
}

// Base64转ArrayBuffer
const base64ToArrayBuffer = (base64) => {
  const binaryString = atob(base64)
  const bytes = new Uint8Array(binaryString.length)
  for (let i = 0; i < binaryString.length; i++) {
    bytes[i] = binaryString.charCodeAt(i)
  }
  return bytes.buffer
}

// 触发自动TTS播报
const triggerAutoTTS = (text) => {
  if (!socket || !isConnected.value) {
    console.warn('⚠️ [TTS] 未连接到服务器，无法播报')
    return
  }
  
  if (!text || !text.trim()) {
    console.warn('⚠️ [TTS] 文本为空，无法播报')
    return
  }
  
  // 🔍 文本长度检查和截断（TTS服务有最大长度限制）
  const MAX_TTS_LENGTH = 300  // TTS最大字符数限制
  let processedText = text.trim()
  let isTruncated = false
  
  if (processedText.length > MAX_TTS_LENGTH) {
    // 智能截断：优先在句子结束处截断
    let truncatePos = MAX_TTS_LENGTH
    const sentenceEndings = ['。', '！', '？', '.', '!', '?', '\n']
    
    // 在最大长度前查找最后一个句子结束符
    for (let i = MAX_TTS_LENGTH - 1; i > MAX_TTS_LENGTH * 0.7; i--) {
      if (sentenceEndings.includes(processedText[i])) {
        truncatePos = i + 1
        break
      }
    }
    
    processedText = processedText.substring(0, truncatePos)
    isTruncated = true
    
    console.log(`⚠️ [TTS] 文本过长 (${text.length}字符)，已截断为 ${processedText.length} 字符`)
    
    // 显示提示信息
    ElMessage.warning({
      message: `文本过长，已截取前${processedText.length}字符进行播报`,
      duration: 3000
    })
  }
  
  console.log('🔊 [TTS] 发送播报请求:', processedText)
  
  // 清空之前的音频块
  autoTTSAudioChunks.value = []
  
  // 发送TTS请求到后端
  socket.emit('start_streaming_tts', {
    text: processedText,
    voice_type: 'BV700_V2_streaming',  // 使用流式语音
    encoding: 'mp3',
    speed_ratio: 1.2,  // 稍快语速
    volume_ratio: 1.0,
    pitch_ratio: 1.0
  })
}

// 播放自动TTS音频
const playAutoTTSAudio = () => {
  if (autoTTSAudioChunks.value.length === 0) {
    return
  }
  
  // 🔒 防止重复播放
  if (isTTSPlaying.value) {
    console.log('⚠️ [TTS] 已有TTS正在播放，跳过')
    return
  }
  
  try {
    console.log('🔊 [TTS] 开始播放，停止ASR会话')
    
    // 🚫 TTS播放期间，完全停止ASR会话（避免超时错误）
    isTTSPlaying.value = true
    
    // 清理所有ASR相关状态标志
    isPausedForCommand.value = false
    isExecutingCommand.value = false
    
    // 停止ASR会话
    if (socket && isConnected.value && isStreamingASR.value) {
      console.log('🛑 停止ASR会话以播放TTS')
      socket.emit('stop_streaming_asr')
      stopStreamingAudioCapture()  // 停止音频采集
    } else {
      // 如果ASR未启动，至少暂停音频采集并更新UI
      pauseAudioCapture()
    }
    
    // 合并所有音频块
    const totalLength = autoTTSAudioChunks.value.reduce((acc, chunk) => acc + chunk.byteLength, 0)
    const mergedArray = new Uint8Array(totalLength)
    
    let offset = 0
    for (const chunk of autoTTSAudioChunks.value) {
      mergedArray.set(new Uint8Array(chunk), offset)
      offset += chunk.byteLength
    }
    
    // 创建Blob并播放
    const blob = new Blob([mergedArray], { type: 'audio/mpeg' })
    const url = URL.createObjectURL(blob)
    
    // 创建音频元素播放
    if (!autoTTSAudioElement.value) {
      autoTTSAudioElement.value = new Audio()
    }
    
    autoTTSAudioElement.value.src = url
    autoTTSAudioElement.value.play()
      .then(() => {
        console.log('🎵 [自动播报] 音频播放中...')
      })
      .catch(error => {
        console.error('❌ [自动播报] 播放失败:', error)
        // 播放失败时也要恢复ASR
        isTTSPlaying.value = false
        isPausedForCommand.value = false
        isExecutingCommand.value = false
        if (isConnected.value && !isStreamingASR.value) {
          console.log('🔄 [TTS] 播放失败，重启ASR会话')
          setTimeout(() => startStreamingASR(), 500)
        } else if (isStreamingASR.value) {
          console.log('✅ [TTS] 播放失败，ASR已在运行，无需重启')
        }
      })
    
    // 播放完成后清理
    autoTTSAudioElement.value.onended = () => {
      console.log('✅ [自动播报] 播放完成')
      URL.revokeObjectURL(url)
      autoTTSAudioChunks.value = []  // 清空音频块
      
      // ✅ TTS播放完成，清理所有状态并重新启动ASR会话
      isTTSPlaying.value = false
      isPausedForCommand.value = false
      isExecutingCommand.value = false
      
      if (isConnected.value && !isStreamingASR.value) {
        console.log('🔄 [TTS] 播放完成，重启ASR会话')
        console.log(`   状态检查: isStreamingASR=${isStreamingASR.value}, isPaused=${isPausedForCommand.value}, isTTS=${isTTSPlaying.value}`)
        setTimeout(() => {
          console.log('🚀 [TTS] 开始重启ASR...')
          startStreamingASR()
        }, 500)
      } else if (isStreamingASR.value) {
        console.log('✅ [TTS] 播放完成，ASR已在运行，无需重启')
      }
    }
    
  } catch (error) {
    console.error('❌ [自动播报] 播放异常:', error)
    // 异常时也要恢复ASR
    isTTSPlaying.value = false
    isPausedForCommand.value = false
    isExecutingCommand.value = false
    if (isConnected.value && !isStreamingASR.value) {
      setTimeout(() => startStreamingASR(), 500)
    } else if (isStreamingASR.value) {
      console.log('✅ [TTS] 播放异常，ASR已在运行，无需重启')
    }
  }
}

// 检查麦克风权限
const checkMicrophonePermission = async () => {
  try {
    await navigator.mediaDevices.getUserMedia({ audio: true })
    console.log('麦克风权限已获取')
  } catch (error) {
    ElMessage.error(t('messages.microphoneError'))
    console.error('麦克风权限获取失败:', error)
  }
}

// 注意：现在使用自动录音，不需要手动点击录音按钮

// 上传音频进行ASR识别
const uploadAudioForASR = async (audioBlob) => {
  try {
    // 将Blob转换为File对象
    const audioFile = new File([audioBlob], 'voice_input.webm', { 
      type: 'audio/webm' 
    })

    // 创建FormData
    const formData = new FormData()
    formData.append('audio', audioFile)

    // 发送到后端进行处理
    const response = await fetch('http://localhost:18080/upload_audio_file', {
      method: 'POST',
      body: formData
    })

    const result = await response.json()
    
    if (result.code === 0 && result.data.asr_result) {
      const recognizedText = result.data.asr_result.full_text
      
      if (recognizedText && recognizedText.trim()) {
        // 只有识别成功且有有效内容时才处理
        console.log('ASR识别成功:', recognizedText)
        
        // 如果WebSocket未连接，手动添加用户消息
        if (!isConnected.value) {
          addMessage('user', recognizedText)
        }
        // 否则等待WebSocket的asr_result事件
        
        // 发送指令处理请求
        await processCommand(recognizedText)
      } else {
        // 识别结果为空，静默处理，不显示错误
        console.log('ASR识别结果为空，继续监听')
        isProcessing.value = false
      }
    } else {
      // ASR识别失败，静默处理，不显示错误提示
      console.log('ASR识别失败，继续监听')
      isProcessing.value = false
    }

  } catch (error) {
    // 网络或其他错误，静默处理
    console.log('音频处理出错，继续监听:', error.message)
    isProcessing.value = false
  }
}

// 处理指令（使用独立的快捷指令接口，完全不依赖WebSocket）
const processCommand = async (command, wasAsrRunning = false) => {
  isQuickCommandProcessing.value = true
  isExecutingCommand.value = true
  // ⏱️ 启动命令执行计时器
  startCommandExecutionTimer()
  
  try {
    // 使用专门的快捷指令接口
    const response = await fetch('http://localhost:18080/quick_command', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ command })
    })

    const result = await response.json()
    
    console.log('📝 快捷指令执行结果:', result)
    
    if (result.success) {
      // 成功执行
      // 优先使用execution_result中的message（包含实际的执行结果，如天气信息）
      const executionMessage = result.data?.execution_result?.message || result.message
      const aiMessage = `✅ ${executionMessage}`
      
      addMessage('ai', aiMessage, {
        success: true,
        message: executionMessage,
        command_type: result.data?.intent || 'unknown',
        execution_result: result.data?.execution_result,
        processing_time: result.data?.processing_time
      })
      
      // 如果有具体的执行结果消息，显示它；否则显示通用消息
      ElMessage.success({
        message: executionMessage || '指令执行成功',
        duration: 3000
      })
      
      // 🔊 自动触发TTS播报执行结果
      if (executionMessage && autoVoicePlayback.value) {
        console.log('🔊 [TTS] 播报执行结果:', executionMessage)
        triggerAutoTTS(executionMessage)
        // 如果有TTS播报，等待播报完成后再恢复ASR
        // TTS播放完成后会自动恢复ASR（见 playAutoTTSAudio 的 onended 事件）
        console.log('ℹ️ [快捷指令] 等待TTS播报完成后自动恢复ASR')
        return  // 提前返回，不在这里恢复ASR
      }
    } else {
      // 执行失败
      const errorMessage = `❌ ${result.message}`
      addMessage('ai', errorMessage, {
        success: false,
        message: result.message
      })
      
      ElMessage.warning({
        message: result.message || '指令执行失败',
        duration: 3000
      })
    }

  } catch (error) {
    // 网络错误
    console.error('⚠️ 指令处理出错:', error)
    const errorMessage = `❌ 网络请求失败: ${error.message}`
    addMessage('ai', errorMessage, {
      success: false,
      message: error.message
    })
    ElMessage.error({
      message: '网络请求失败，请检查后端服务是否运行',
      duration: 3000
    })
  } finally {
    isQuickCommandProcessing.value = false
    isExecutingCommand.value = false
    // ⏱️ 停止命令执行计时器
    stopCommandExecutionTimer()
    
    // 🔄 如果之前ASR在运行，且没有触发TTS播报，则恢复ASR
    if (wasAsrRunning && isConnected.value && !isStreamingASR.value && !isTTSPlaying.value) {
      console.log('🔄 [快捷指令] 指令执行完成，恢复ASR识别')
      setTimeout(() => {
        if (isConnected.value && !isStreamingASR.value && !isTTSPlaying.value) {
          startStreamingASR()
          console.log('✅ [快捷指令] ASR已恢复')
        }
      }, 500)
    }
  }
}

// 发送快捷指令（完全独立，不需要WebSocket连接）
const sendQuickCommand = async (command) => {
  // 防止重复点击
  if (isQuickCommandProcessing.value) {
    ElMessage.warning('正在处理中，请稍候...')
    return
  }
  
  console.log('🚀 执行快捷指令:', command)
  
  // 🛑 立即设置快捷指令处理状态，阻止音频发送
  isQuickCommandProcessing.value = true
  
  // 🛑 快捷指令执行期间，停止ASR识别
  let wasAsrRunning = false
  if (isStreamingASR.value && socket && isConnected.value) {
    console.log('🛑 [快捷指令] 停止ASR识别')
    wasAsrRunning = true
    stopStreamingASR()
    // 等待ASR完全停止
    await new Promise(resolve => setTimeout(resolve, 300))
  }
  
  // 在执行指令前先释放处理状态（因为processCommand会重新设置）
  isQuickCommandProcessing.value = false
  
  // 🔊 如果WebSocket未连接，自动建立连接（用于TTS播报）
  if (!socket || !isConnected.value) {
    console.log('⚠️ [快捷指令] WebSocket未连接，自动建立连接以支持TTS播报')
    try {
      initWebSocket()
      // 等待连接建立（最多3秒）
      let waitCount = 0
      while (!isConnected.value && waitCount < 30) {
        await new Promise(resolve => setTimeout(resolve, 100))
        waitCount++
      }
      if (!isConnected.value) {
        console.warn('⚠️ [快捷指令] WebSocket连接超时，将不播报语音')
      } else {
        console.log('✅ [快捷指令] WebSocket连接成功')
      }
    } catch (error) {
      console.error('❌ [快捷指令] WebSocket连接失败:', error)
    }
  }
  
  // 添加用户消息
  addMessage('user', command)
  
  // 执行指令
  await processCommand(command, wasAsrRunning)
}

// 添加消息
const addMessage = (type, text, result = null) => {
  const message = {
    id: Date.now(),
    type,
    text,
    result,
    timestamp: new Date()
  }
  
  chatMessages.value.push(message)
  
  // 滚动到底部
  nextTick(() => {
    if (chatContainer.value) {
      chatContainer.value.scrollTop = chatContainer.value.scrollHeight
    }
  })
}

// 清空聊天记录
const clearChat = () => {
  chatMessages.value = []
  ElMessage.success(t('messages.chatCleared'))
}

// 格式化时间
const formatTime = (date) => {
  return date.toLocaleTimeString('zh-CN', {
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit'
  })
}

// 格式化消息时间（用于当前对话显示）
const formatMessageTime = (date) => {
  if (!date) return ''
  return formatTime(date)
}

// 获取最新的消息（用于小屏幕当前对话显示）
const latestMessage = computed(() => {
  if (chatMessages.value.length === 0) return null;
  if (chatMessages.value.length === 1) return null;
  return chatMessages.value[chatMessages.value.length - 1]
})

// 连接控制
const toggleConnection = async () => {
  if (isConnected.value) {
    // 断开连接
    disconnectWebSocket()
  } else {
    // 建立连接
    isConnecting.value = true
    try {
      await initWebSocket()
    } catch (error) {
      isConnecting.value = false
      ElMessage.error(t('messages.connectFailed') + ': ' + error.message)
    }
  }
}

// 断开WebSocket连接
const disconnectWebSocket = () => {
  // 先停止流式ASR
  if (isStreamingASR.value) {
    stopStreamingASR()
  }
  
  // 停止旧的自动录音（如果还在运行）
  stopAutoRecording()
  
  // 清理ASR错误恢复定时器
  if (asrErrorRecoveryTimer) {
    clearTimeout(asrErrorRecoveryTimer)
    asrErrorRecoveryTimer = null
  }
  
  if (socket) {
    socket.disconnect()
    socket = null
  }
  
  isConnected.value = false
  isConnecting.value = false
  // 不在这里显示提示，由 socket.on('disconnect') 事件统一处理
}

// 自动录音相关变量
let autoRecordingTimer = null
let currentStream = null

// 开始自动语音识别
const startAutoRecording = async () => {
  if (!isConnected.value) return
  
  try {
    // 获取麦克风权限
    currentStream = await navigator.mediaDevices.getUserMedia({ 
      audio: {
        echoCancellation: true,
        noiseSuppression: true,
        autoGainControl: true
      }
    })

    // 创建MediaRecorder
    mediaRecorder = new MediaRecorder(currentStream, {
      mimeType: 'audio/webm;codecs=opus'
    })

    audioChunks = []

    mediaRecorder.ondataavailable = (event) => {
      if (event.data.size > 0) {
        audioChunks.push(event.data)
      }
    }

    mediaRecorder.onstop = () => {
      if (audioChunks.length > 0) {
        const audioBlob = new Blob(audioChunks, { type: 'audio/webm' })
        // 异步处理ASR，不阻塞录音循环
        uploadAudioForASR(audioBlob).finally(() => {
          // 无论ASR成功还是失败，都继续下一轮录音
          if (isConnected.value) {
            setTimeout(() => {
              startNextRecording()
            }, 500) // 短暂间隔后开始下一轮
          }
        })
      } else {
        // 没有音频数据，直接继续下一轮
        if (isConnected.value) {
          setTimeout(() => {
            startNextRecording()
          }, 500)
        }
      }
    }

    // 开始录音
    startNextRecording()
    
  } catch (error) {
    ElMessage.error(t('messages.microphoneAccessError') + ': ' + error.message)
    console.error('自动录音启动失败:', error)
  }
}

// 开始下一轮录音
const startNextRecording = () => {
  if (!isConnected.value) return
  
  // 不再检查isProcessing状态，允许并行处理
  audioChunks = []
  isRecording.value = true
  
  if (mediaRecorder && mediaRecorder.state === 'inactive') {
    mediaRecorder.start()
    
    // 设置录音时长（比如3秒一段）
    autoRecordingTimer = setTimeout(() => {
      if (mediaRecorder && mediaRecorder.state === 'recording') {
        mediaRecorder.stop()
        isRecording.value = false
      }
    }, 3000)
  }
}

// 停止自动录音
const stopAutoRecording = () => {
  if (autoRecordingTimer) {
    clearTimeout(autoRecordingTimer)
    autoRecordingTimer = null
  }
  
  if (mediaRecorder && mediaRecorder.state !== 'inactive') {
    mediaRecorder.stop()
  }
  
  if (currentStream) {
    currentStream.getTracks().forEach(track => track.stop())
    currentStream = null
  }
  
  isRecording.value = false
  isListening.value = false
}

// ============= 流式ASR相关函数 =============

// 更新流式识别消息
const updateStreamingMessage = (text, isFinal) => {
  if (!text || !text.trim()) return
  
  console.log(`🔄 updateStreamingMessage: text="${text}", isFinal=${isFinal}, 当前流式消息:`, streamingMessage.value)
  
  if (isFinal) {
    // 最终结果：固定当前消息
    if (streamingMessage.value) {
      // 检查文本是否改变，避免重复
      if (streamingMessage.value.text !== text) {
        streamingMessage.value.text = text
      }
      streamingMessage.value.isStreaming = false
      console.log('✅ 固定流式消息为最终结果')
      streamingMessage.value = null  // 清空引用，下次识别创建新消息
    } else {
      // 如果没有流式消息，检查是否已经存在相同内容
      const lastMessage = chatMessages.value[chatMessages.value.length - 1]
      if (!lastMessage || lastMessage.text !== text) {
        console.log('📝 创建新的最终消息')
        addMessage('user', text)
      } else {
        console.log('⚠️ 跳过重复的最终消息')
      }
    }
  } else {
    // 中间结果：更新或创建流式消息
    if (streamingMessage.value) {
      // 检查文本是否改变，避免不必要的更新
      if (streamingMessage.value.text !== text) {
        console.log('🔄 更新流式消息文本')
        streamingMessage.value.text = text
        
        // 更新时也滚动到底部，确保用户始终看到最新内容
        nextTick(() => {
          if (chatContainer.value) {
            chatContainer.value.scrollTop = chatContainer.value.scrollHeight
          }
        })
      }
    } else {
      // 创建新的流式消息
      console.log('🆕 创建新的流式消息')
      const message = {
        id: 'streaming-' + Date.now(),
        type: 'user',
        text: text,
        isStreaming: true,  // 标记为流式识别中
        timestamp: new Date()
      }
      chatMessages.value.push(message)
      streamingMessage.value = message
      
      // 滚动到底部
      nextTick(() => {
        if (chatContainer.value) {
          chatContainer.value.scrollTop = chatContainer.value.scrollHeight
        }
      })
    }
  }
}

// 开始流式音频采集（使用AudioContext直接获取PCM数据）
const startStreamingAudioCapture = async () => {
  try {
    console.log('开始流式音频采集...')
    
    // 获取麦克风权限
    const stream = await navigator.mediaDevices.getUserMedia({ 
      audio: {
        echoCancellation: true,
        noiseSuppression: true,
        autoGainControl: true
      }
    })

    // 创建AudioContext（16kHz采样率）
    streamingAudioContext = new (window.AudioContext || window.webkitAudioContext)({
      sampleRate: 16000  // 火山ASR需要16kHz
    })

    const source = streamingAudioContext.createMediaStreamSource(stream)
    
    // 创建音量分析器
    volumeAnalyser = streamingAudioContext.createAnalyser()
    volumeAnalyser.fftSize = 256
    volumeAnalyser.smoothingTimeConstant = 0.8
    
    // 使用ScriptProcessorNode处理音频数据
    // bufferSize: 4096 约等于 256ms (4096/16000)
    const processor = streamingAudioContext.createScriptProcessor(4096, 1, 1)
    
    processor.onaudioprocess = (e) => {
      // 如果正在执行命令、已暂停、快捷指令处理中、或TTS播放中，不发送音频数据
      if (!isStreamingASR.value || !socket || isPausedForCommand.value || isTTSPlaying.value || isQuickCommandProcessing.value) {
        // 添加调试：打印阻止原因（但限制频率，避免刷屏）
        if (Math.random() < 0.01) {  // 只打印1%的事件
          console.log(`⚠️ 音频处理被阻止: ASR=${isStreamingASR.value}, socket=${!!socket}, paused=${isPausedForCommand.value}, TTS=${isTTSPlaying.value}, quickCmd=${isQuickCommandProcessing.value}`)
        }
        return
      }
      
      // 获取音频数据（单声道，Float32Array）
      const inputData = e.inputBuffer.getChannelData(0)
      
      // 转换为16bit PCM (Int16Array)
      const pcmData = floatTo16BitPCM(inputData)
      
      // 转换为Base64发送到后端
      const base64Audio = arrayBufferToBase64(pcmData.buffer)
      
      // 发送音频数据
      socket.emit('streaming_audio_chunk', {
        audio: base64Audio
      })
    }
    
    // 连接音频处理链
    source.connect(volumeAnalyser)
    volumeAnalyser.connect(processor)
    processor.connect(streamingAudioContext.destination)
    
    // 启动音量检测
    startVolumeDetection()
    
    // 保存引用以便后续清理
    streamingMediaRecorder = {
      stream: stream,
      processor: processor,
      source: source
    }
    
    isRecording.value = true
    console.log('流式音频采集已启动（PCM格式）')
    
  } catch (error) {
    console.error('启动流式音频采集失败:', error)
    ElMessage.error(t('messages.microphoneAccessError') + ': ' + error.message)
  }
}

// 暂停音频采集（不关闭连接）
const pauseAudioCapture = () => {
  // 先更新UI状态
  isRecording.value = false
  audioVolume.value = 0  // 清零音量显示
  
  if (!streamingMediaRecorder || !streamingMediaRecorder.processor) {
    console.log('⏸️ 音频采集已暂停（无活动会话）')
    return
  }
  
  try {
    // 暂停音频处理（断开处理器）
    streamingMediaRecorder.processor.disconnect()
    
    // 停止音量检测
    stopVolumeDetection()
    
    console.log('⏸️ 音频采集已暂停')
  } catch (error) {
    console.error('暂停音频采集失败:', error)
  }
}

// 恢复音频采集
const resumeAudioCapture = () => {
  if (!streamingMediaRecorder || !streamingAudioContext || !volumeAnalyser) {
    console.log('⚠️ 无法恢复录音：组件未初始化')
    return
  }
  
  try {
    // 重新连接音频处理链
    if (streamingMediaRecorder.source && streamingMediaRecorder.processor) {
      streamingMediaRecorder.source.connect(volumeAnalyser)
      volumeAnalyser.connect(streamingMediaRecorder.processor)
      streamingMediaRecorder.processor.connect(streamingAudioContext.destination)
    }
    
    // 重新启动音量检测
    startVolumeDetection()
    
    isRecording.value = true
    console.log('▶️ 音频采集已恢复')
  } catch (error) {
    console.error('恢复音频采集失败:', error)
  }
}

// 启动音量检测
const startVolumeDetection = () => {
  if (!volumeAnalyser) return
  
  const bufferLength = volumeAnalyser.frequencyBinCount
  const dataArray = new Uint8Array(bufferLength)
  let smoothedVolume = 0  // 平滑后的音量值
  
  const checkVolume = () => {
    if (!volumeAnalyser || !isStreamingASR.value || isPausedForCommand.value || isTTSPlaying.value || isQuickCommandProcessing.value) {
      return
    }
    
    // 获取音频频域数据
    volumeAnalyser.getByteFrequencyData(dataArray)
    
    // 计算平均音量，重点关注中低频区域（人声主要频率）
    let sum = 0
    const relevantRange = Math.min(bufferLength, 80) // 只取前80个频段（约0-5kHz）
    for (let i = 0; i < relevantRange; i++) {
      sum += dataArray[i]
    }
    const average = sum / relevantRange
    
    // 将音量映射到0-100范围，使用多级缩放使变化更明显
    let volume = 0
    if (average > 0) {
      // 应用对数缩放，使小音量更明显，大音量不会过度
      volume = Math.min(100, Math.pow(average / 100, 0.7) * 100)
      // 设置最小阈值，过滤背景噪音
      if (volume < 5) {
        volume = 0
      }
    }
    
    // 平滑处理：使用指数移动平均，减少抖动
    smoothedVolume = smoothedVolume * 0.7 + volume * 0.3
    
    // 更新响应式音量值
    audioVolume.value = smoothedVolume
  }
  
  // 每50ms检测一次音量，确保流畅的动画效果
  volumeCheckInterval = setInterval(checkVolume, 50)
}

// 停止音量检测
const stopVolumeDetection = () => {
  if (volumeCheckInterval) {
    clearInterval(volumeCheckInterval)
    volumeCheckInterval = null
  }
  audioVolume.value = 0
}

// Float32 转 Int16 PCM
const floatTo16BitPCM = (float32Array) => {
  const int16Array = new Int16Array(float32Array.length)
  for (let i = 0; i < float32Array.length; i++) {
    // 将 -1.0 到 1.0 的浮点数转换为 -32768 到 32767 的整数
    const s = Math.max(-1, Math.min(1, float32Array[i]))
    int16Array[i] = s < 0 ? s * 0x8000 : s * 0x7FFF
  }
  return int16Array
}

// ArrayBuffer 转 Base64
const arrayBufferToBase64 = (buffer) => {
  const bytes = new Uint8Array(buffer)
  let binary = ''
  for (let i = 0; i < bytes.byteLength; i++) {
    binary += String.fromCharCode(bytes[i])
  }
  return window.btoa(binary)
}

// 停止流式音频采集
const stopStreamingAudioCapture = () => {
  try {
    // 停止音量检测
    stopVolumeDetection()
    
    if (streamingMediaRecorder) {
      // 断开音频处理链
      if (streamingMediaRecorder.processor) {
        streamingMediaRecorder.processor.disconnect()
      }
      if (streamingMediaRecorder.source) {
        streamingMediaRecorder.source.disconnect()
      }
      
      // 停止所有音频轨道
      if (streamingMediaRecorder.stream) {
        streamingMediaRecorder.stream.getTracks().forEach(track => track.stop())
      }
    }
    
    // 断开音量分析器
    if (volumeAnalyser) {
      volumeAnalyser.disconnect()
      volumeAnalyser = null
    }
    
    // 关闭AudioContext
    if (streamingAudioContext) {
      streamingAudioContext.close()
      streamingAudioContext = null
    }
    
    streamingMediaRecorder = null
    isRecording.value = false
    
    console.log('流式音频采集已停止')
    
  } catch (error) {
    console.error('停止流式音频采集失败:', error)
  }
}

// 启动流式ASR
const startStreamingASR = () => {
  if (!socket || !isConnected.value) {
    ElMessage.warning(t('voiceInput.pleaseConnect'))
    return
  }
  
  // 🔒 防止重复启动
  if (isStreamingASR.value) {
    console.log('⚠️ [ASR] ASR已在运行，跳过启动请求')
    return
  }
  
  console.log('请求启动流式ASR...')
  socket.emit('start_streaming_asr')
}

// 停止流式ASR
const stopStreamingASR = () => {
  if (!socket || !isConnected.value) {
    return
  }
  
  console.log('请求停止流式ASR...')
  socket.emit('stop_streaming_asr')
  isStreamingASR.value = false
  
  // 清理状态
  isExecutingCommand.value = false
  isPausedForCommand.value = false
}

// 重启流式ASR（用于命令执行后恢复）
const restartStreamingASR = async () => {
  if (!socket || !isConnected.value) {
    console.log('⚠️ 无法重启：未连接到服务器')
    return
  }
  
  try {
    console.log('🔄 开始重启流式ASR会话...')
    
    // 步骤1: 停止旧的音频采集
    console.log('  1️⃣ 停止旧的音频采集...')
    stopStreamingAudioCapture()
    
    // 步骤2: 通知后端停止旧会话
    console.log('  2️⃣ 停止旧的ASR会话...')
    socket.emit('stop_streaming_asr')
    
    // 步骤3: 等待一小段时间确保旧会话完全关闭
    await new Promise(resolve => setTimeout(resolve, 500))
    
    // 步骤4: 启动新的ASR会话
    console.log('  3️⃣ 启动新的ASR会话...')
    socket.emit('start_streaming_asr')
    isStreamingASR.value = true
    
    console.log('✅ 流式ASR会话重启完成')
    
  } catch (error) {
    console.error('❌ 重启流式ASR失败:', error)
    ElMessage.error(t('messages.asrError'))
    
    // 失败时尝试完全重置
    isStreamingASR.value = false
    stopStreamingAudioCapture()
  }
}

// ============= 流式ASR相关函数结束 =============

// ============= 生成漫画相关函数 =============

// 打开漫画生成对话框
const openComicDialog = () => {
  // 重置表单
  comicForm.value.userInput = ''
  comicForm.value.numImages = 3
  comicDialogVisible.value = true
  console.log('🎨 打开漫画生成对话框')
}

// 生成漫画（使用改进后的接口）
const generateComic = async () => {
  if (!comicForm.value.userInput.trim()) {
    ElMessage.warning('请输入漫画主题')
    return
  }
  
  isGeneratingComic.value = true
  
  try {
    console.log('🎨 开始生成漫画...')
    console.log('用户输入:', comicForm.value.userInput)
    console.log('生成数量:', comicForm.value.numImages)
    
    // 在聊天框中添加用户消息
    addMessage('user', `生成漫画：${comicForm.value.userInput} (${comicForm.value.numImages}张)`)
    
    // 获取客户端ID（用于TTS播报）
    const clientId = socket?.id || null
    
    // 调用增强版文生图接口（已包含大模型改写和TTS播报）
    console.log('🎨 调用文生图接口（包含LLM改写和TTS）...')
    
    const imageResponse = await fetch('http://localhost:18080/text_to_image/generate', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        prompt: comicForm.value.userInput,
        num_images: comicForm.value.numImages,
        size: '2K',
        watermark: true,
        response_format: 'url',
        client_id: clientId,
        enable_llm_rewrite: true,  // 启用大模型改写
        enable_tts: true  // 启用语音播报
      })
    })
    
    const imageResult = await imageResponse.json()
    console.log('🎨 接口返回:', imageResult)
    
    // 处理结果
    if (imageResult.code === 0 && imageResult.data) {
      console.log('✅ 漫画生成成功!')
      
      // 保存结果
      comicResult.value = {
        success: true,
        message: imageResult.message || `成功生成 ${imageResult.data.count} 张漫画`,
        images: imageResult.data.images || [],
        prompt: imageResult.data.rewritten_prompt || imageResult.data.original_prompt
      }
      
      // 关闭输入对话框，显示结果对话框
      comicDialogVisible.value = false
      comicResultVisible.value = true
      
      ElMessage.success('漫画生成成功！')
      
      // 在聊天框中添加AI消息并显示图片
      const aiMessage = {
        id: Date.now(),
        type: 'ai',
        text: `已成功生成 ${imageResult.data.count} 张漫画！`,
        images: imageResult.data.images,  // 添加图片数据
        timestamp: new Date(),
        metadata: {
          original_prompt: imageResult.data.original_prompt,
          rewritten_prompt: imageResult.data.rewritten_prompt,
          tts_played: imageResult.data.tts_played
        }
      }
      
      chatMessages.value.push(aiMessage)
      
      // 滚动到底部
      nextTick(() => {
        if (chatContainer.value) {
          chatContainer.value.scrollTop = chatContainer.value.scrollHeight
        }
      })
      
    } else {
      console.error('❌ 漫画生成失败:', imageResult.message)
      
      // 保存失败结果
      comicResult.value = {
        success: false,
        message: imageResult.message || '漫画生成失败，请重试',
        images: [],
        prompt: ''
      }
      
      // 显示结果对话框（显示错误）
      comicDialogVisible.value = false
      comicResultVisible.value = true
      
      ElMessage.error('漫画生成失败：' + imageResult.message)
      
      // 在聊天框中显示错误消息
      addMessage('ai', `❌ 漫画生成失败：${imageResult.message}`)
    }
    
  } catch (error) {
    console.error('❌ 生成漫画出错:', error)
    
    ElMessage.error('生成漫画失败：' + error.message)
    
    comicResult.value = {
      success: false,
      message: error.message || '网络错误，请检查后端服务',
      images: [],
      prompt: ''
    }
    
    // 显示错误结果
    comicDialogVisible.value = false
    comicResultVisible.value = true
    
    // 在聊天框中显示错误消息
    addMessage('ai', `❌ 生成漫画失败：${error.message}`)
    
  } finally {
    isGeneratingComic.value = false
  }
}

// 下载图片
const downloadImage = async (url, index) => {
  try {
    console.log('📥 开始下载图片:', url)
    
    // 通过代理下载（避免跨域问题）
    const response = await fetch(url)
    const blob = await response.blob()
    
    // 创建下载链接
    const downloadUrl = URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.href = downloadUrl
    link.download = `comic_${Date.now()}_${index}.png`
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
    
    // 释放URL对象
    URL.revokeObjectURL(downloadUrl)
    
    ElMessage.success('图片下载成功')
    
  } catch (error) {
    console.error('❌ 下载图片失败:', error)
    ElMessage.error('图片下载失败：' + error.message)
  }
}

// ============= 生成漫画相关函数结束 =============

</script>

<style scoped>
/* Ant Design 风格样式 */
.voice-control-interface {
  width: 100vw;
  height: 100vh;
  background: #f0f2f5;
  position: fixed;
  top: 0;
  left: 0;
  margin: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
}

/* 顶部标题栏 */
.header-bar {
  background: #fff;
  border-bottom: 1px solid #f0f0f0;
  padding: 0 24px;
  height: 64px;
  display: flex;
  align-items: center;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}

.header-content {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.logo-section {
  display: flex;
  align-items: center;
  gap: 12px;
}

.logo-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
  box-shadow: 0 2px 8px rgba(24, 144, 255, 0.3);
  transition: all 0.3s ease;
}

.logo-avatar:hover {
  transform: scale(1.05);
  box-shadow: 0 4px 16px rgba(24, 144, 255, 0.5);
}

.app-title {
  margin: 0;
  font-size: 20px;
  font-weight: 600;
  color: #262626;
}

/* MCP提示 - 顶部标题栏 */
.mcp-tip-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 16px;
  background: linear-gradient(135deg, #e6f7ff 0%, #f0f9ff 100%);
  border: 1px solid #91d5ff;
  border-radius: 20px;
  animation: tipSlideIn 0.5s ease-out;
  flex-shrink: 0;
  margin: 0 auto;
}

.tip-icon-header {
  color: #1890ff;
  font-size: 16px;
  flex-shrink: 0;
}

.tip-text-header {
  font-size: 13px;
  color: #0050b3;
  font-weight: 500;
  white-space: nowrap;
}

@keyframes tipSlideIn {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 顶部右侧按钮组 */
.header-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

.header-settings-btn {
  border: 1px solid #d9d9d9;
  background: #fff;
  transition: all 0.3s ease;
}

.header-settings-btn:hover {
  border-color: #1890ff;
  color: #1890ff;
  transform: rotate(90deg);
}

.connection-status {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: 6px;
  font-size: 14px;
  background: #fff2f0;
  color: #ff4d4f;
  border: 1px solid #ffccc7;
}

.connection-status.connected {
  background: #f6ffed;
  color: #52c41a;
  border-color: #b7eb8f;
}

/* 主内容区域 */
.main-content {
  flex: 1;
  display: flex;
  padding: 24px 24px 0 24px; /* 底部不留padding */
  gap: 24px;
  overflow: hidden;
  min-height: 0; /* 修复flex滚动问题 */
}

/* 左侧面板 */
.left-panel {
  width: 400px;
  height: 100%; /* 占满整个高度 */
  display: flex;
  flex-direction: column;
  min-height: 0; /* 修复flex滚动问题 */
  overflow-x: hidden; /* 隐藏横向滚动条 */
}

.voice-section {
  display: flex;
  flex-direction: column;
  gap: 16px;
  height: 100%; /* 占满整个高度 */
  flex: 1; /* 填充整个left-panel */
  min-height: 0; /* 修复flex滚动问题 */
  overflow: hidden; /* 不显示滚动条 */
  padding-bottom: 24px; /* 底部内边距 */
}

/* 卡片样式 */
.voice-card,
.quick-actions-card,
.chat-card {
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
  border: 1px solid #f0f0f0;
  display: flex;
  flex-direction: column;
}

.voice-card {
  flex: 1; /* 占用空间 */
  min-height: 280px; /* 最小高度 */
}

.quick-actions-card {
  flex: 0.8; /* 占用一定空间 */
  min-height: 240px; /* 最小高度 */
  overflow: visible; /* 允许内容显示 */
  display: flex;
  flex-direction: column;
}

/* 当前对话文本（仅小屏幕显示，一行简洁显示） */
.current-dialog-text {
  display: none; /* 默认隐藏 */
  position: relative;
  z-index: 10; /* 确保不被遮挡 */
  margin-top: 12px;
  padding: 10px 16px;
  background: #f0f2f5;
  border-radius: 6px;
  font-size: 13px;
  line-height: 1.5;
  color: #262626;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  animation: fadeInUp 0.3s ease-out;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}

/* 流式生成光标 */
.streaming-cursor {
  display: inline-block;
  width: 2px;
  height: 1em;
  background: #1890ff;
  margin-left: 2px;
  animation: cursorBlink 1s infinite;
  vertical-align: text-bottom;
}

@keyframes cursorBlink {
  0%, 49% {
    opacity: 1;
  }
  50%, 100% {
    opacity: 0;
  }
}

.card-header {
  padding: 10px 20px;
  border-bottom: 1px solid #f0f0f0;
  flex-shrink: 0; /* 防止标题被压缩 */
}

.card-header h3 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  color: #262626;
}

.card-subtitle {
  margin: 3px 0 0 0;
  font-size: 13px;
  color: #8c8c8c;
}

/* 语音控制区域 */
.voice-circle-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 30px 24px;
  gap: 14px;
  flex: 1; /* 占满语音卡片的剩余空间 */
  justify-content: center; /* 垂直居中 */
}

.voice-circle {
  width: 110px;
  height: 110px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid #d9d9d9;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform 0.1s ease, box-shadow 0.1s ease, border-color 0.3s ease, background 0.3s ease;
  position: relative;
  overflow: hidden;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  --volume-scale: 1;
  --ripple-scale: 2;
  --wave-height: 16px;
}

.voice-circle:hover {
  border-color: #1890ff;
  box-shadow: 0 4px 12px rgba(24, 144, 255, 0.2);
}

.voice-circle.recording {
  background: #fff1f0;
  border-color: #ff4d4f;
  transform: scale(var(--volume-scale));
  box-shadow: 0 4px 20px rgba(255, 77, 79, calc(0.3 + var(--volume-scale) * 0.2));
}

.voice-circle.processing {
  background: #fffbe6;
  border-color: #faad14;
}

.circle-avatar-wrapper {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
  transition: all 0.3s ease;
}

/* 可点击状态 */
.circle-avatar-wrapper.clickable {
  cursor: pointer;
}

.circle-avatar-wrapper.clickable:hover {
  transform: scale(1.05);
}

.circle-avatar-wrapper.clickable:hover .circle-avatar {
  filter: brightness(1.15) drop-shadow(0 0 12px rgba(24, 144, 255, 0.6));
}

.circle-avatar-wrapper.clickable:active {
  transform: scale(0.98);
}

.circle-avatar {
  width: 80%;
  height: 80%;
  border-radius: 50%;
  object-fit: cover;
  transition: all 0.3s ease;
  animation: avatarBreath 4s ease-in-out infinite;
}

/* 呼吸动画 */
@keyframes avatarBreath {
  0%, 100% {
    transform: scale(1);
    filter: brightness(1);
  }
  50% {
    transform: scale(1.05);
    filter: brightness(1.1);
  }
}

/* 录音时的脉冲效果 */
.voice-circle.recording .circle-avatar {
  animation: avatarPulse 0.8s ease-in-out infinite;
  filter: brightness(1.2) drop-shadow(0 0 10px rgba(255, 77, 79, 0.8));
}

@keyframes avatarPulse {
  0%, 100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.1);
  }
}

/* 处理中的遮罩层 */
.processing-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(250, 173, 20, 0.2);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 3;
}

.processing-icon {
  color: #faad14;
  animation: rotate 2s linear infinite;
}

/* 连接中的遮罩层 */
.connecting-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(24, 144, 255, 0.2);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 3;
}

.connecting-icon {
  color: #1890ff;
  animation: rotate 2s linear infinite;
}

/* 连接状态 */
.voice-circle.connected {
  border-color: #52c41a;
  background: #f6ffed;
}

.voice-circle.connected .circle-avatar {
  filter: brightness(1.1) drop-shadow(0 0 8px rgba(82, 196, 26, 0.5));
}

/* 断开连接状态 */
.voice-circle.disconnected {
  border-color: #d9d9d9;
  background: #f5f5f5;
}

.voice-circle.disconnected .circle-avatar {
  filter: grayscale(0.5) brightness(0.8);
  animation: none;
}

/* 监听状态 */
.voice-circle.listening .circle-avatar {
  animation: avatarGlow 2s ease-in-out infinite;
}

@keyframes avatarGlow {
  0%, 100% {
    filter: brightness(1) drop-shadow(0 0 5px rgba(82, 196, 26, 0.3));
  }
  50% {
    filter: brightness(1.15) drop-shadow(0 0 15px rgba(82, 196, 26, 0.6));
  }
}

.recording-animation {
  display: flex;
  gap: 3px;
  align-items: center;
}

.wave {
  width: 3px;
  height: var(--wave-height);
  background: #ff4d4f;
  border-radius: 2px;
  animation: wave 0.8s infinite ease-in-out;
}

.wave:nth-child(2) {
  animation-delay: 0.1s;
}

.wave:nth-child(3) {
  animation-delay: 0.2s;
}

.processing-icon {
  animation: rotate 2s linear infinite;
}

.ripple-effect {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  pointer-events: none;
}

.ripple {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100%;
  height: 100%;
  border: 2px solid #ff4d4f;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  animation: ripple calc(2s - var(--volume-scale) * 0.3s) infinite ease-out;
}

.ripple:nth-child(2) {
  animation-delay: 0.5s;
}

.ripple:nth-child(3) {
  animation-delay: 1s;
}

.status-text {
  font-size: 13px;
  color: #595959;
  text-align: center;
}

.recording-text {
  color: #ff4d4f;
  font-weight: 500;
}

.processing-text {
  color: #faad14;
  font-weight: 500;
}

.listening-text {
  color: #52c41a;
  font-weight: 500;
}

.disconnected-text {
  color: #bfbfbf;
  font-weight: 400;
}

.paused-text {
  color: #faad14;
  font-weight: 500;
}

.tts-playing-text {
  color: #1890ff;
  font-weight: 500;
}

/* 执行时间显示 */
.execution-time {
  display: inline-block;
  margin-left: 4px;
  padding: 2px 8px;
  background: rgba(250, 173, 20, 0.15);
  border-radius: 10px;
  font-size: 12px;
  font-weight: 600;
  color: #faad14;
  animation: timePulse 1s ease-in-out infinite;
}

@keyframes timePulse {
  0%, 100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.85;
    transform: scale(1.05);
  }
}

/* 音量指示器 */
.volume-indicator {
  width: 100%;
  max-width: 260px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: center;
  animation: fadeInUp 0.3s ease;
}

.volume-bar-container {
  width: 100%;
  height: 5px;
  background: #f0f0f0;
  border-radius: 2.5px;
  overflow: hidden;
  position: relative;
}

.volume-bar {
  height: 100%;
  background: linear-gradient(90deg, #52c41a 0%, #faad14 50%, #ff4d4f 100%);
  border-radius: 2.5px;
  transition: width 0.1s ease;
  box-shadow: 0 0 8px rgba(255, 77, 79, 0.5);
}

.volume-text {
  font-size: 12px;
  color: #595959;
  font-weight: 500;
}

/* 快捷操作 */
.quick-actions {
  padding: 12px 20px 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1; /* 占满推荐功能卡片的剩余空间 */
  overflow: visible; /* 显示所有按钮 */
  max-height: none; /* 移除最大高度限制 */
  justify-content: center; /* 垂直居中 */
}

.quick-btn {
  width: 100%;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center; /* 居中对齐内容 */
  flex-shrink: 0; /* 防止按钮被压缩 */
  margin-left: 0 !important; /* 覆盖 Element Plus 的默认左边距 */
  font-size: 13px;
}

/* 确保Element Plus按钮内部内容居中 */
.quick-btn :deep(.el-button__content) {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
}

.quick-btn :deep(.el-icon) {
  margin: 0;
}

/* 右侧面板 */
.right-panel {
  flex: 1;
  height: 100%; /* 占满整个高度 */
  display: flex;
  flex-direction: column;
  min-height: 0; /* 修复flex滚动问题 */
  padding-bottom: 24px; /* 底部内边距 */
}

.chat-card {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0; /* 修复flex滚动问题 */
  overflow: hidden; /* 确保内容不会溢出 */
}

.chat-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 24px;
  border-bottom: 1px solid #f0f0f0;
}

.chat-header h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: #262626;
}

.clear-btn {
  color: #8c8c8c;
}

.clear-btn:hover {
  color: #ff4d4f;
}

/* 聊天消息区域 */
.chat-messages {
  flex: 1;
  min-height: 0; /* 修复flex滚动问题 - 关键！ */
  padding: 16px 24px;
  overflow-y: auto;
  overflow-x: hidden; /* 防止横向溢出 */
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.message-item {
  display: flex;
  gap: 12px;
  animation: fadeInUp 0.3s ease;
}

.message-item.user {
  flex-direction: row-reverse;
}

.message-avatar {
  flex-shrink: 0;
}

.user-avatar {
  background: #1890ff;
  color: #fff;
}

.ai-avatar {
  background: transparent;
  color: #fff;
  border: 2px solid #52c41a;
  box-shadow: 0 2px 8px rgba(82, 196, 26, 0.3);
  animation: avatarFadeIn 0.5s ease;
}

.ai-avatar img {
  animation: avatarRotateIn 0.5s ease;
}

@keyframes avatarFadeIn {
  from {
    opacity: 0;
    transform: scale(0.8);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes avatarRotateIn {
  from {
    transform: rotate(-180deg) scale(0.5);
    opacity: 0;
  }
  to {
    transform: rotate(0) scale(1);
    opacity: 1;
  }
}

.message-content {
  flex: 1;
  min-width: 0;
  max-width: 70%;
}

.message-item.user .message-content {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}

.message-bubble {
  padding: 12px 16px;
  border-radius: 8px;
  word-wrap: break-word;
  position: relative;
}

.message-bubble.user {
  background: #1890ff;
  color: #fff;
}

.message-bubble.user.streaming {
  background: linear-gradient(90deg, #1890ff, #40a9ff);
  animation: streamingPulse 1.5s ease-in-out infinite;
  border: 2px solid #69c0ff;
}

.message-bubble.ai {
  background: #f5f5f5;
  color: #262626;
}

.message-bubble.ai.streaming {
  background: linear-gradient(90deg, #f5f5f5, #e8e8e8);
  animation: streamingPulse 1.5s ease-in-out infinite;
  border: 2px solid #d9d9d9;
}

.streaming-indicator {
  display: inline-block;
  animation: dots 1.5s steps(4, end) infinite;
  font-weight: bold;
  margin-left: 4px;
}

.message-text {
  margin: 0;
  line-height: 1.5;
}

.message-time {
  font-size: 12px;
  color: #8c8c8c;
  margin-top: 4px;
}

.message-item.user .message-time {
  text-align: right;
}

.command-result {
  margin-top: 8px;
}

/* 聊天框图片展示 */
.message-images {
  margin-top: 12px;
  max-width: 100%;
}

.images-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 8px;
  max-width: 400px;
}

.message-item.ai .images-grid {
  justify-content: flex-start;
}

.message-item.user .images-grid {
  justify-content: flex-end;
}

.image-item {
  position: relative;
  border-radius: 8px;
  overflow: hidden;
  background: #f5f5f5;
  transition: all 0.3s ease;
  cursor: pointer;
}

.image-item:hover {
  transform: translateY(-4px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

.image-number {
  position: absolute;
  top: 4px;
  left: 4px;
  background: rgba(24, 144, 255, 0.9);
  color: #fff;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 600;
  z-index: 2;
}

.message-image {
  width: 100%;
  height: 120px;
  display: block;
  transition: all 0.3s ease;
}

.image-loading,
.image-error-small {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 120px;
  color: #8c8c8c;
  font-size: 12px;
}

.image-loading .el-icon,
.image-error-small .el-icon {
  font-size: 24px;
}

/* 深色主题适配 */
html.dark-theme .image-item {
  background: #262626;
}

html.dark-theme .image-loading,
html.dark-theme .image-error-small {
  color: #8c8c8c;
}

/* 缩小指令执行结果样式 */
.command-result :deep(.el-alert) {
  padding: 8px 12px; /* 减小内边距 */
  font-size: 13px; /* 减小字体 */
}

.command-result :deep(.el-alert__title) {
  font-size: 13px; /* 减小标题字体 */
  font-weight: 500;
}

.command-result :deep(.el-alert__description) {
  font-size: 12px; /* 减小描述字体 */
  margin-top: 2px; /* 减小间距 */
}

.command-result :deep(.el-alert__icon) {
  font-size: 16px; /* 减小图标 */
}

.empty-chat {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.empty-icon {
  color: #d9d9d9;
}

/* 设置对话框内容 */
.settings-content {
  padding: 20px 0;
}

.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  width: 100%;
}

.setting-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 0;
  border-bottom: 1px solid #f0f0f0;
}

.setting-item:last-child {
  border-bottom: none;
}

.setting-label {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 15px;
  font-weight: 500;
  color: #262626;
}

.setting-label .el-icon {
  font-size: 20px;
  color: #1890ff;
}

/* 动画 */
@keyframes pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.05); }
}

@keyframes wave {
  0%, 100% { 
    height: var(--wave-height); 
    opacity: 0.8;
  }
  50% { 
    height: calc(var(--wave-height) * 1.5); 
    opacity: 1;
  }
}

@keyframes rotate {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes ripple {
  0% {
    width: 100%;
    height: 100%;
    opacity: 0.8;
  }
  100% {
    width: calc(100% * var(--ripple-scale));
    height: calc(100% * var(--ripple-scale));
    opacity: 0;
  }
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes streamingPulse {
  0%, 100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.85;
    transform: scale(1.02);
  }
}

@keyframes dots {
  0%, 20% {
    content: '.';
  }
  40% {
    content: '..';
  }
  60%, 100% {
    content: '...';
  }
}

/* 响应式设计 */
@media (max-width: 1200px) {
  .left-panel {
    width: 350px;
  }
}

@media (max-width: 768px) {
  /* 小屏幕下允许整体滚动 */
  .voice-control-interface {
    overflow-y: auto;
    overflow-x: hidden;
    height: 100vh;
  }
  
  /* 隐藏顶部标题栏 */
  .header-bar {
    display: none;
  }
  
  /* 隐藏右侧对话记录面板 */
  .right-panel {
    display: none;
  }
  
  /* 显示当前对话文本 */
  .current-dialog-text {
    display: block;
  }
  
  /* 隐藏小屏幕下的提示文字 */
  .card-subtitle {
    display: none;
  }
  
  .status-text {
    display: none;
  }
  
  .main-content {
    flex-direction: column;
    padding: 16px;
    gap: 16px;
    overflow-y: auto;
    overflow-x: hidden;
    height: auto;
    min-height: calc(100vh - 32px);
  }
  
  .left-panel {
    width: 100%;
    height: auto;
    min-height: auto;
  }
  
  .voice-section {
    height: auto;
    min-height: auto;
    overflow: visible;
  }
  
  .voice-card {
    min-height: auto;
    padding-bottom: 16px;
  }
  
  .voice-circle-container {
    padding: 20px 0;
  }
  
  .quick-actions-card {
    min-height: 200px;
  }
  
  .voice-circle {
    width: 90px;
    height: 90px;
  }
  
  .message-content {
    max-width: 85%;
  }
}

@media (max-width: 480px) {
  .main-content {
    padding: 12px;
    min-height: calc(100vh - 24px);
  }
  
  .voice-circle {
    width: 80px;
    height: 80px;
  }
  
  .voice-card {
    min-height: auto;
    padding-bottom: 12px;
  }
  
  .voice-circle-container {
    padding: 16px 0;
  }
  
  .quick-actions-card {
    min-height: 180px;
  }
  
  .quick-actions {
    padding: 12px 16px;
  }
  
  .chat-messages {
    padding: 12px 16px;
  }
  
  /* 调整卡片内边距 */
  .card-header {
    padding: 10px 16px;
  }
  
  /* 调整标题字体大小 */
  .app-title {
    font-size: 18px;
  }
  
  .card-header h3 {
    font-size: 14px;
  }
  
  /* 更紧凑的布局 */
  .quick-action-item {
    padding: 10px 12px;
  }
  
  /* 实时消息字体略小 */
  .current-dialog-text {
    font-size: 12px;
    padding: 8px 12px;
  }
}

/* 超小屏幕优化 (小于375px) */
@media (max-width: 375px) {
  .main-content {
    padding: 8px;
    min-height: calc(100vh - 16px);
  }
  
  .voice-circle {
    width: 70px;
    height: 70px;
  }
  
  .voice-card {
    min-height: auto;
    padding-bottom: 10px;
  }
  
  .voice-circle-container {
    padding: 12px 0;
  }
  
  .quick-actions-card {
    min-height: 160px;
  }
  
  .card-header {
    padding: 8px 12px;
  }
  
  .card-header h3 {
    font-size: 13px;
  }
  
  .current-dialog-text {
    font-size: 11px;
    padding: 6px 10px;
    margin-top: 8px;
  }
}

/* 滚动条样式 */
.chat-messages::-webkit-scrollbar {
  width: 6px;
}

.chat-messages::-webkit-scrollbar-track {
  background: #f5f5f5;
  border-radius: 3px;
}

.chat-messages::-webkit-scrollbar-thumb {
  background: #d9d9d9;
  border-radius: 3px;
}

.chat-messages::-webkit-scrollbar-thumb:hover {
  background: #bfbfbf;
}

/* 小屏幕下的整体滚动条样式 */
@media (max-width: 768px) {
  .voice-control-interface::-webkit-scrollbar {
    width: 6px;
  }

  .voice-control-interface::-webkit-scrollbar-track {
    background: #f5f5f5;
    border-radius: 3px;
  }

  .voice-control-interface::-webkit-scrollbar-thumb {
    background: #d9d9d9;
    border-radius: 3px;
  }

  .voice-control-interface::-webkit-scrollbar-thumb:hover {
    background: #bfbfbf;
  }
  
  /* 平滑滚动 */
  .voice-control-interface {
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
  }
}

/* ============= 新手引导样式 ============= */

/* 引导遮罩层（透明容器） */
.guide-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 9999;
  pointer-events: auto;
}

/* 四个遮罩区域 */
.guide-mask-top,
.guide-mask-right,
.guide-mask-bottom,
.guide-mask-left {
  position: fixed;
  background: rgba(0, 0, 0, 0.6);
  pointer-events: auto;
  z-index: 9999;
  animation: fadeIn 0.3s ease;
}

/* 引导高亮框 */
.guide-highlight {
  position: fixed;
  background: transparent;
  border: 3px solid #1890ff;
  border-radius: 8px;
  box-shadow: 0 0 20px rgba(24, 144, 255, 0.8);
  pointer-events: none;
  z-index: 10001;
  transition: all 0.3s ease;
  animation: highlightPulse 2s infinite ease-in-out;
}

@keyframes highlightPulse {
  0%, 100% {
    box-shadow: 0 0 20px rgba(24, 144, 255, 0.8);
  }
  50% {
    box-shadow: 0 0 30px rgba(24, 144, 255, 1);
  }
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* 引导提示框 */
.guide-tooltip {
  position: fixed;
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
  padding: 0;
  min-width: 320px;
  max-width: 400px;
  z-index: 10002;
  pointer-events: auto;
  animation: slideIn 0.3s ease;
}

@keyframes slideIn {
  from {
    opacity: 0;
    transform: translateY(-20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.guide-tooltip-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid #f0f0f0;
}

.guide-step {
  font-size: 14px;
  color: #1890ff;
  font-weight: 600;
}

.guide-skip-btn {
  color: #8c8c8c;
  padding: 0;
}

.guide-skip-btn:hover {
  color: #1890ff;
}

.guide-tooltip-content {
  padding: 20px 24px;
}

.guide-tooltip-content h3 {
  margin: 0 0 12px 0;
  font-size: 18px;
  font-weight: 600;
  color: #262626;
}

.guide-tooltip-content p {
  margin: 0;
  font-size: 14px;
  line-height: 1.6;
  color: #595959;
}

.guide-tooltip-footer {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  padding: 16px 20px;
  border-top: 1px solid #f0f0f0;
}

/* 响应式调整 */
@media (max-width: 768px) {
  .guide-tooltip {
    min-width: 280px;
    max-width: calc(100vw - 40px);
  }
  
  .guide-tooltip-content h3 {
    font-size: 16px;
  }
  
  .guide-tooltip-content p {
    font-size: 13px;
  }
}
</style>

<!-- 深色主题全局样式（不带 scoped） -->
<style>
html.dark-theme {
  background: #141414;
  color: #e8e8e8;
}

html.dark-theme .voice-control-interface {
  background: #141414;
}

html.dark-theme .header-bar {
  background: #1f1f1f;
  border-bottom-color: #303030;
}

html.dark-theme .app-title {
  color: #e8e8e8;
}

/* MCP提示深色主题 - 顶部 */
html.dark-theme .mcp-tip-header {
  background: linear-gradient(135deg, #111d2c 0%, #0f1419 100%);
  border-color: #1890ff;
}

html.dark-theme .tip-icon-header {
  color: #40a9ff;
}

html.dark-theme .tip-text-header {
  color: #91d5ff;
}

html.dark-theme .header-settings-btn {
  background: #262626;
  border-color: #434343;
  color: #e8e8e8;
}

html.dark-theme .header-settings-btn:hover {
  border-color: #1890ff;
  color: #1890ff;
}

html.dark-theme .voice-card,
html.dark-theme .quick-actions-card,
html.dark-theme .chat-card {
  background: #1f1f1f;
  border-color: #303030;
}

html.dark-theme .card-header {
  border-bottom-color: #303030;
}

html.dark-theme .card-header h3 {
  color: #e8e8e8;
}

html.dark-theme .card-subtitle {
  color: #8c8c8c;
}

html.dark-theme .chat-header {
  border-bottom-color: #303030;
}

html.dark-theme .chat-header h3 {
  color: #e8e8e8;
}

html.dark-theme .voice-circle {
  background: #262626;
  border-color: #434343;
}

html.dark-theme .voice-circle.connected {
  background: #162312;
  border-color: #52c41a;
}

html.dark-theme .voice-circle.connected .circle-avatar {
  filter: brightness(1.1) drop-shadow(0 0 10px rgba(82, 196, 26, 0.6));
}

html.dark-theme .logo-avatar {
  box-shadow: 0 2px 8px rgba(64, 169, 255, 0.4);
}

html.dark-theme .logo-avatar:hover {
  box-shadow: 0 4px 16px rgba(64, 169, 255, 0.6);
}

html.dark-theme .voice-circle.disconnected {
  background: #1f1f1f;
  border-color: #434343;
}

/* 暗色主题 - 可点击头像效果 */
html.dark-theme .circle-avatar-wrapper.clickable:hover .circle-avatar {
  filter: brightness(1.2) drop-shadow(0 0 12px rgba(64, 169, 255, 0.7));
}

/* 暗色主题 - 连接中的遮罩层 */
html.dark-theme .connecting-overlay {
  background: rgba(24, 144, 255, 0.3);
}

html.dark-theme .message-bubble.ai {
  background: #262626;
  color: #e8e8e8;
}

html.dark-theme .message-time {
  color: #8c8c8c;
}

/* 暗色主题 - 当前对话文本 */
html.dark-theme .current-dialog-text {
  background: #262626;
  color: #e8e8e8;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

html.dark-theme .streaming-cursor {
  background: #40a9ff;
}

html.dark-theme .setting-label {
  color: #e8e8e8;
}

html.dark-theme .setting-item {
  border-bottom-color: #303030;
}

html.dark-theme .chat-messages::-webkit-scrollbar-track {
  background: #1f1f1f;
}

html.dark-theme .chat-messages::-webkit-scrollbar-thumb {
  background: #434343;
}

html.dark-theme .chat-messages::-webkit-scrollbar-thumb:hover {
  background: #595959;
}

/* Element Plus Dialog 深色主题 */
html.dark-theme .el-dialog {
  background: #1f1f1f;
  border: 1px solid #303030;
}

html.dark-theme .el-dialog__header {
  background: #1f1f1f;
  border-bottom: 1px solid #303030;
}

html.dark-theme .el-dialog__title {
  color: #e8e8e8;
}

html.dark-theme .el-dialog__body {
  background: #1f1f1f;
  color: #e8e8e8;
}

html.dark-theme .el-dialog__footer {
  background: #1f1f1f;
  border-top: 1px solid #303030;
}

html.dark-theme .el-dialog__close {
  color: #8c8c8c;
}

html.dark-theme .el-dialog__close:hover {
  color: #e8e8e8;
}

/* Element Plus Select 深色主题 */
html.dark-theme .el-select .el-input__wrapper {
  background: #262626;
  border-color: #434343;
  box-shadow: none;
}

html.dark-theme .el-select .el-input__inner {
  color: #e8e8e8;
}

html.dark-theme .el-select .el-input__wrapper:hover {
  border-color: #1890ff;
}

/* Element Plus Switch 深色主题 */
html.dark-theme .el-switch {
  border-color: #434343;
}

html.dark-theme .el-switch__core {
  background: #434343;
  border-color: #434343;
}

html.dark-theme .el-switch.is-checked .el-switch__core {
  background: #1890ff;
  border-color: #1890ff;
}

/* Element Plus Segmented 深色主题 */
html.dark-theme .el-segmented {
  background: #262626;
  border-color: #434343;
}

html.dark-theme .el-segmented__item {
  color: #8c8c8c;
}

html.dark-theme .el-segmented__item:hover {
  color: #e8e8e8;
}

html.dark-theme .el-segmented__item.is-selected {
  background: #1890ff;
  color: #fff;
}

/* Element Plus Button 深色主题 */
html.dark-theme .el-button {
  background: #262626;
  border-color: #434343;
  color: #e8e8e8;
}

html.dark-theme .el-button:hover {
  background: #303030;
  border-color: #1890ff;
  color: #1890ff;
}

html.dark-theme .el-button--primary {
  background: #1890ff;
  border-color: #1890ff;
  color: #fff;
}

html.dark-theme .el-button--primary:hover {
  background: #40a9ff;
  border-color: #40a9ff;
  color: #fff;
}

/* Element Plus Select Dropdown 深色主题 */
html.dark-theme .el-select-dropdown {
  background: #262626;
  border-color: #434343;
}

html.dark-theme .el-select-dropdown__item {
  color: #e8e8e8;
}

html.dark-theme .el-select-dropdown__item:hover {
  background: #303030;
}

html.dark-theme .el-select-dropdown__item.selected {
  background: #1890ff;
  color: #fff;
}

/* Element Plus Popper 深色主题 */
html.dark-theme .el-popper {
  background: #262626;
  border-color: #434343;
}

html.dark-theme .el-popper.is-dark {
  background: #262626;
  color: #e8e8e8;
}

html.dark-theme .el-popper__arrow::before {
  background: #262626;
  border-color: #434343;
}

/* Element Plus Overlay 深色主题 */
html.dark-theme .el-overlay {
  background-color: rgba(0, 0, 0, 0.7);
}

/* Element Plus Input 深色主题 */
html.dark-theme .el-input__wrapper {
  background: #262626;
  border-color: #434343;
  box-shadow: none;
}

html.dark-theme .el-input__inner {
  color: #e8e8e8;
}

html.dark-theme .el-input__wrapper:hover {
  border-color: #1890ff;
}

html.dark-theme .el-input__wrapper.is-focus {
  border-color: #1890ff;
  box-shadow: 0 0 0 1px rgba(24, 144, 255, 0.2);
}

/* Element Plus Icon 深色主题 */
html.dark-theme .el-icon {
  color: #e8e8e8;
}

/* 新手引导 - 深色主题 */
html.dark-theme .guide-tooltip {
  background: #1f1f1f;
  border: 1px solid #303030;
}

html.dark-theme .guide-tooltip-header {
  border-bottom-color: #303030;
}

html.dark-theme .guide-tooltip-content h3 {
  color: #e8e8e8;
}

html.dark-theme .guide-tooltip-content p {
  color: #bfbfbf;
}

html.dark-theme .guide-tooltip-footer {
  border-top-color: #303030;
}

html.dark-theme .guide-highlight {
  border-color: #40a9ff;
  box-shadow: 0 0 20px rgba(64, 169, 255, 0.8);
}

html.dark-theme .guide-mask-top,
html.dark-theme .guide-mask-right,
html.dark-theme .guide-mask-bottom,
html.dark-theme .guide-mask-left {
  background: rgba(0, 0, 0, 0.75);
}

/* 执行时间显示 - 深色主题 */
html.dark-theme .execution-time {
  background: rgba(250, 173, 20, 0.25);
  color: #ffc53d;
}

/* 隐藏 Vue DevTools */
.vue-devtools,
.vue-devtools__anchor,
.__vue-devtools-iframe__,
#__vue-devtools-container__ {
  display: none !important;
  visibility: hidden !important;
  opacity: 0 !important;
  pointer-events: none !important;
}

/* ============= 生成漫画样式 ============= */

/* 漫画对话框内容 */
.comic-dialog-content {
  padding: 10px 0;
}

/* 漫画结果内容 */
.comic-result-content {
  padding: 10px 0;
}

/* 漫画图片网格布局 */
.comic-images-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 24px;
  padding: 10px 0;
}

/* 单个漫画图片项 */
.comic-image-item {
  position: relative;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  transition: all 0.3s ease;
  background: #f5f5f5;
}

.comic-image-item:hover {
  transform: translateY(-8px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}

/* 图片序号 */
.comic-image-number {
  position: absolute;
  top: 12px;
  left: 12px;
  background: rgba(24, 144, 255, 0.9);
  color: #fff;
  padding: 6px 16px;
  border-radius: 20px;
  font-size: 14px;
  font-weight: 600;
  z-index: 2;
  box-shadow: 0 2px 8px rgba(24, 144, 255, 0.3);
}

/* 漫画图片 */
.comic-image {
  width: 100%;
  height: 300px;
  display: block;
  cursor: pointer;
  transition: all 0.3s ease;
}

.comic-image:hover {
  opacity: 0.9;
}

/* 图片占位符 */
.image-placeholder,
.image-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  height: 300px;
  color: #8c8c8c;
  font-size: 14px;
}

.image-placeholder .el-icon,
.image-error .el-icon {
  font-size: 48px;
  animation: pulse 2s ease-in-out infinite;
}

/* 图片操作按钮 */
.comic-image-actions {
  position: absolute;
  bottom: 12px;
  right: 12px;
  z-index: 2;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.comic-image-item:hover .comic-image-actions {
  opacity: 1;
}

.comic-image-actions .el-button {
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(10px);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

/* 提示词信息 */
.comic-prompt-info {
  margin-top: 24px;
  padding: 16px;
  background: #f5f5f5;
  border-radius: 8px;
}

.comic-prompt-info .el-text {
  line-height: 1.6;
  word-break: break-all;
}

/* 深色主题适配 */
html.dark-theme .comic-image-item {
  background: #262626;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

html.dark-theme .comic-image-item:hover {
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}

html.dark-theme .image-placeholder,
html.dark-theme .image-error {
  color: #8c8c8c;
}

html.dark-theme .comic-prompt-info {
  background: #1f1f1f;
}

html.dark-theme .comic-image-actions .el-button {
  background: rgba(31, 31, 31, 0.95);
  color: #e8e8e8;
}

/* 响应式设计 */
@media (max-width: 1200px) {
  .comic-images-grid {
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 20px;
  }
}

@media (max-width: 768px) {
  .comic-images-grid {
    grid-template-columns: 1fr;
    gap: 16px;
  }
  
  .comic-image {
    height: 250px;
  }
  
  .image-placeholder,
  .image-error {
    height: 250px;
  }
}

/* 动画 */
@keyframes pulse {
  0%, 100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.6;
    transform: scale(1.1);
  }
}
</style>
